8361 lines
346 KiB
C#
8361 lines
346 KiB
C#
using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity;
|
|
using Qdp.ComputeService.Data.CommonModels.ValuationParams.FixedIncome;
|
|
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
|
|
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Options;
|
|
using Qdp.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;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using YLErp.BLL;
|
|
using YLErp.BLL.Calculation;
|
|
using YLErp.BLL.Calculation.Engine;
|
|
using YLErp.Commons;
|
|
using YLErp.DBModels;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.Model;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.Modules.SkewMapVolModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.CalculationModule
|
|
{
|
|
/// <summary>
|
|
/// 期权计算类
|
|
/// </summary>
|
|
public 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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算香草期权的估值与风险
|
|
/// </summary>
|
|
/// <param name="marketProxy">MarketProxy对象</param>
|
|
/// <param name="valueDate">估值日期</param>
|
|
/// <param name="underlyingTicker">标的代码</param>
|
|
/// <param name="underlyingInstrumentType">标的类型</param>
|
|
/// <param name="strike">行权价</param>
|
|
/// <param name="timeToMaturityDays">用户指定距离到期日的天数,可以为小数</param>
|
|
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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
engineName,
|
|
discountCurveName,
|
|
timeToMaturityDays,
|
|
isForwardTrade,
|
|
riskFreeRateOverride: riskFreeRateOverride,
|
|
dividendRateOverride: dividendRateOverride,
|
|
pricingRequest: pricingRequest);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算香草期权的估值与风险,本函数会根据是否为SkewMap波动率来调整波动率获取方式
|
|
/// </summary>
|
|
public 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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
engineName,
|
|
discountCurveName,
|
|
timeToMaturityDays,
|
|
isForwardTrade,
|
|
ignoreSkewMap,
|
|
riskFreeRateOverride: riskFreeRateOverride,
|
|
dividendRateOverride: dividendRateOverride,
|
|
pricingRequest: pricingRequest);
|
|
}
|
|
#endregion
|
|
|
|
#region 障碍期权 (单障碍和双障碍都支持)
|
|
public 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<Date, double> dividends = null,
|
|
string position = "Buy",
|
|
double barrierShift = 0.0,
|
|
string barrierStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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();
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exerciseDates = new Date[] { temp_exerciseDate };
|
|
if (observationDates == null)
|
|
{
|
|
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"),
|
|
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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
public 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<Date, double> dividends = null,
|
|
string position = "Buy",
|
|
double barrierShift = 0.0,
|
|
string barrierStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string position = "Buy",
|
|
double barrierShift = 0.0,
|
|
string barrierStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string position = "Buy",
|
|
double barrierShift = 0.0,
|
|
string barrierStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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);
|
|
}
|
|
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
//return null;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 二元期权
|
|
|
|
public 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<Date, double> dividends = null,
|
|
string binaryRebateType = "AtEnd",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
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<Date, double> dividends = null,
|
|
string binaryRebateType = "AtEnd",
|
|
string binaryOptionReplicationStrategy = "None",
|
|
double replicationShiftSize = 0.0,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string binaryRebateType = "AtEnd",
|
|
string binaryOptionReplicationStrategy = "None",
|
|
double replicationShiftSize = 0.0,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string binaryRebateType = "AtEnd",
|
|
string binaryOptionReplicationStrategy = "None",
|
|
double replicationShiftSize = 0.0,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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);
|
|
}
|
|
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
//return null;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 亚式期权
|
|
public 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<Date, double> dividends = null,
|
|
string averagingPeriodStartDate = null,
|
|
string observationDateStr = null,
|
|
string fixings = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
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<Date, double> dividends = null,
|
|
string fixings = null,
|
|
string averagingPeriodStartDate = null,
|
|
string observationDateStr = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string fixings = null,
|
|
string averagingPeriodStartDate = null,
|
|
string observationDateStr = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string fixings = null,
|
|
string averagingPeriodStartDate = null,
|
|
string observationDateStr = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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 彩虹期权
|
|
public 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 commodityFuturesPreciseTimeMode = 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: commodityFuturesPreciseTimeMode,
|
|
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),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor
|
|
};
|
|
}
|
|
|
|
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 commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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 commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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 commodityFuturesPreciseTimeMode = false,
|
|
string engineName = null,
|
|
string discountCurveName = null,
|
|
bool isForwardTrade = false,
|
|
double riskFreeRateOverride = double.NaN,
|
|
double dividendRateOverride = double.NaN,
|
|
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
|
|
{
|
|
if (volSurfaceNames == null)
|
|
{
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode).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);
|
|
}
|
|
|
|
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
//return null;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 价差期权
|
|
public 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 commodityFuturesPreciseTimeMode = 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: commodityFuturesPreciseTimeMode,
|
|
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 commodityFuturesPreciseTimeMode = false,
|
|
string engineName = null,
|
|
string discountCurveName = null,
|
|
double riskFreeRateOverride = double.NaN,
|
|
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
|
|
{
|
|
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
|
|
|
|
if (volSurfaceNames == null)
|
|
{
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode).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);
|
|
|
|
|
|
try
|
|
{
|
|
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
|
|
};
|
|
|
|
valueResult.UnderlyingCode = underlyingTickers[0];
|
|
valueResult.UnderlyingCode2 = underlyingTickers[1];
|
|
if (underlyingTickers.Length > 2)
|
|
{
|
|
valueResult.UnderlyingCode3 = underlyingTickers[2];
|
|
}
|
|
if (underlyingTickers.Length > 3)
|
|
{
|
|
valueResult.UnderlyingCode4 = underlyingTickers[3];
|
|
}
|
|
|
|
return valueResult;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
|
|
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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<double>();
|
|
|
|
// 先获取返回的结果矩阵中的对角线上的元素,对应的是每个标的的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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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;
|
|
}
|
|
}
|
|
|
|
public 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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
#endregion
|
|
|
|
#region 亚式合成价差期权
|
|
public 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<Date, double> dividends = null,
|
|
string averagingPeriodStartDate = null,
|
|
string fixings = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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<Date, double>() :
|
|
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: commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string fixings = null,
|
|
string averagingPeriodStartDate = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string fixings = null,
|
|
string averagingPeriodStartDate = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string fixings = null,
|
|
string averagingPeriodStartDate = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends = null,
|
|
string knockInOutStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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<Date, double> dividends,
|
|
string knockInOutStatus,
|
|
bool hasNightMarket,
|
|
bool commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
discountCurveName,
|
|
timeToMaturityDays,
|
|
engineName,
|
|
riskFreeRateOverride: riskFreeRateOverride,
|
|
dividendRateOverride: dividendRateOverride,
|
|
pricingRequest: pricingRequest);
|
|
}
|
|
}
|
|
|
|
public 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 commodityFuturesPreciseTimeMode = 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();
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exerciseDates = new Date[] { temp_exerciseDate };
|
|
if (observationDates == null)
|
|
{
|
|
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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
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<Date, double> dividends,
|
|
string knockInOutStatus,
|
|
bool hasNightMarket,
|
|
bool commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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);
|
|
}
|
|
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
//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<autocall_observation> happenedObservations,
|
|
string kiOptionType = "Put",
|
|
double dividendRate = 0.0,
|
|
bool isMoneynessOptionData = false,
|
|
double initialSpotPrice = 0.0,
|
|
string observationDates = null,
|
|
Dictionary<Date, double> dividends = null,
|
|
string knockInOutStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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<autocall_observation> happenedObservations,
|
|
string kiOptionType = "Put",
|
|
double dividendRate = 0.0,
|
|
bool isMoneynessOptionData = false,
|
|
double initialSpotPrice = 0.0,
|
|
string observationDates = null,
|
|
Dictionary<Date, double> dividends = null,
|
|
string knockInOutStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
discountCurveName: discountCurveName,
|
|
timeToMaturityDays: timeToMaturityDays,
|
|
ignoreSkewMap: ignoreSkewMap,
|
|
engineName: engineName,
|
|
riskFreeRateOverride: riskFreeRateOverride,
|
|
dividendRateOverride: dividendRateOverride);
|
|
}
|
|
}
|
|
|
|
public 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<autocall_observation> 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();
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exerciseDates = new Date[] { temp_exerciseDate };
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
|
|
}
|
|
}
|
|
|
|
if (koObservationDates == null)
|
|
{
|
|
var monthlyDates = new List<Date>();
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
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<autocall_observation> happenedObservations,
|
|
string kiOptionType = "Put",
|
|
double dividendRate = 0.0,
|
|
bool isMoneynessOptionData = false,
|
|
double initialSpotPrice = 0.0,
|
|
Dictionary<Date, double> dividends = null,
|
|
string knockInOutStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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);
|
|
}
|
|
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
//return null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 雪球期权
|
|
public static TradeValueResult ValueSnowballTrade(
|
|
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,
|
|
bool isAnnualized,
|
|
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,
|
|
string observationDates = null,
|
|
Dictionary<Date, double> dividends = null,
|
|
string knockInOutStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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 ValueSnowballTrade(
|
|
VolSurfaceInitializerSingleton.VolConstructionType,
|
|
marketProxy: marketProxy,
|
|
valueDate: valueDate,
|
|
underlyingCode: underlyingCode,
|
|
underlyingInstrumentType: underlyingInstrumentType,
|
|
startDate: startDate,
|
|
endDate: endDate,
|
|
callPut: callPut,
|
|
koBarrier: koBarrier,
|
|
kiBarrier: kiBarrier,
|
|
koObservationDateStr: koObservationDateStr,
|
|
observationDateStr: observationDateStr,
|
|
coupon: coupon,
|
|
protectionRate: participationRate,
|
|
principalRate: principalRate,
|
|
spotPrice: spotPrice,
|
|
notional: notional,
|
|
volSurfaceName: volSurfaceName,
|
|
riskFreeRate: riskFreeRate,
|
|
buySell: buySell,
|
|
exerciseDate: exerciseDate,
|
|
annualizeFactor: annualizeFactor,
|
|
strike: strike,
|
|
spreadStrikeAtKO: spreadStrikeAtKO,
|
|
spreadStrikeAtMaturity: spreadStrikeAtMaturity,
|
|
isFixedCoupon: isFixedCoupon,
|
|
kORebate: kORebate,
|
|
kOBarrierAdjustStep: kOBarrierAdjustStep,
|
|
useOptionPayoffAtKO: useOptionPayoffAtKO,
|
|
useOptionPayoffAtMaturity: useOptionPayoffAtMaturity,
|
|
annualizedOptionPayoff: annualizedOptionPayoff,
|
|
couponDayCount: couponDayCount,
|
|
dividendRate: dividendRate,
|
|
isMoneynessOptionData: isMoneynessOptionData,
|
|
initialSpotPrice: initialSpotPrice,
|
|
observationDates: observationDates,
|
|
dividends: dividends,
|
|
knockInOutStatus: knockInOutStatus,
|
|
hasNightMarket: hasNightMarket,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
discountCurveName: discountCurveName,
|
|
timeToMaturityDays: timeToMaturityDays,
|
|
ignoreSkewMap: ignoreSkewMap,
|
|
engineName: engineName,
|
|
riskFreeRateOverride: riskFreeRateOverride,
|
|
dividendRateOverride: dividendRateOverride);
|
|
}
|
|
|
|
private static TradeValueResult ValueSnowballTrade(
|
|
VolConstructionType volConstructionType,
|
|
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 protectionRate,
|
|
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,
|
|
string observationDates = null,
|
|
Dictionary<Date, double> dividends = null,
|
|
string knockInOutStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = false,
|
|
string discountCurveName = null,
|
|
double timeToMaturityDays = double.NaN,
|
|
bool ignoreSkewMap = false,
|
|
string engineName = null,
|
|
bool useNoKiSnowball = false,
|
|
double koStrike = double.NaN,
|
|
string koOptionType = "Call",
|
|
double riskFreeRateOverride = double.NaN,
|
|
double dividendRateOverride = double.NaN)
|
|
{
|
|
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
|
|
{
|
|
return ValueSnowball(
|
|
marketProxy: marketProxy,
|
|
valueDate: valueDate,
|
|
underlyingCode: underlyingCode,
|
|
underlyingInstrumentType: underlyingInstrumentType,
|
|
startDate: startDate,
|
|
endDate: endDate,
|
|
callPut: callPut,
|
|
koBarrier: koBarrier,
|
|
kiBarrier: kiBarrier,
|
|
koObservationDateStr: koObservationDateStr,
|
|
observationDateStr: observationDateStr,
|
|
coupon: coupon,
|
|
participationRate: protectionRate,
|
|
principalRate: principalRate,
|
|
spotPrice: spotPrice,
|
|
notional: notional,
|
|
volSurfaceName: volSurfaceName,
|
|
riskFreeRate: riskFreeRate,
|
|
buySell: buySell,
|
|
exerciseDate: exerciseDate,
|
|
annualizeFactor: annualizeFactor,
|
|
strike: strike,
|
|
spreadStrikeAtKO: spreadStrikeAtKO,
|
|
spreadStrikeAtMaturity: spreadStrikeAtMaturity,
|
|
isFixedCoupon: isFixedCoupon,
|
|
kORebate: kORebate,
|
|
kOBarrierAdjustStep: kOBarrierAdjustStep,
|
|
useOptionPayoffAtKO: useOptionPayoffAtKO,
|
|
useOptionPayoffAtMaturity: useOptionPayoffAtMaturity,
|
|
annualizedOptionPayoff: annualizedOptionPayoff,
|
|
couponDayCount: couponDayCount,
|
|
dividendRate: dividendRate,
|
|
isMoneynessOptionData: isMoneynessOptionData,
|
|
initialSpotPrice: initialSpotPrice,
|
|
dividends: dividends,
|
|
knockInOutStatus: knockInOutStatus,
|
|
hasNightMarket: hasNightMarket,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
discountCurveName: discountCurveName,
|
|
timeToMaturityDays: timeToMaturityDays,
|
|
ignoreSkewMap: ignoreSkewMap,
|
|
engineName: engineName,
|
|
riskFreeRateOverride: riskFreeRateOverride,
|
|
dividendRateOverride: dividendRateOverride,
|
|
useNoKiSnowball: useNoKiSnowball,
|
|
koStrike: koStrike,
|
|
koOptionType: koOptionType);
|
|
}
|
|
else
|
|
{
|
|
var tradeId = Guid.NewGuid().ToString();
|
|
SetSkewMapVol(
|
|
marketProxy,
|
|
valueDate,
|
|
underlyingCode,
|
|
exerciseDate,
|
|
koBarrier,
|
|
TradeCalcHelper.IsBuy(buySell),
|
|
QdpHelper.IsCall(callPut),
|
|
tradeId,
|
|
spotPrice);
|
|
|
|
return ValueSnowball(
|
|
marketProxy: marketProxy,
|
|
valueDate: valueDate,
|
|
underlyingCode: underlyingCode,
|
|
underlyingInstrumentType: underlyingInstrumentType,
|
|
startDate: startDate,
|
|
endDate: endDate,
|
|
callPut: callPut,
|
|
koBarrier: koBarrier,
|
|
kiBarrier: kiBarrier,
|
|
koObservationDateStr: koObservationDateStr,
|
|
observationDateStr: observationDateStr,
|
|
coupon: coupon,
|
|
participationRate: protectionRate,
|
|
principalRate: principalRate,
|
|
spotPrice: spotPrice,
|
|
notional: notional,
|
|
volSurfaceName: tradeId.GenerateVolSurfaceKey(),
|
|
riskFreeRate: riskFreeRate,
|
|
buySell: buySell,
|
|
exerciseDate: exerciseDate,
|
|
annualizeFactor: annualizeFactor,
|
|
strike: strike,
|
|
spreadStrikeAtKO: spreadStrikeAtKO,
|
|
spreadStrikeAtMaturity: spreadStrikeAtMaturity,
|
|
isFixedCoupon: isFixedCoupon,
|
|
kORebate: kORebate,
|
|
kOBarrierAdjustStep: kOBarrierAdjustStep,
|
|
useOptionPayoffAtKO: useOptionPayoffAtKO,
|
|
useOptionPayoffAtMaturity: useOptionPayoffAtMaturity,
|
|
annualizedOptionPayoff: annualizedOptionPayoff,
|
|
couponDayCount: couponDayCount,
|
|
dividendRate: dividendRate,
|
|
isMoneynessOptionData: isMoneynessOptionData,
|
|
initialSpotPrice: initialSpotPrice,
|
|
dividends: dividends,
|
|
knockInOutStatus: knockInOutStatus,
|
|
hasNightMarket: hasNightMarket,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
discountCurveName: discountCurveName,
|
|
timeToMaturityDays: timeToMaturityDays,
|
|
ignoreSkewMap: ignoreSkewMap,
|
|
engineName: engineName,
|
|
riskFreeRateOverride: riskFreeRateOverride,
|
|
dividendRateOverride: dividendRateOverride,
|
|
useNoKiSnowball: useNoKiSnowball,
|
|
koStrike: koStrike,
|
|
koOptionType: koOptionType);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="useNoKiSnowball">是否构造年化权利金模式的保本雪球</param>
|
|
/// <returns></returns>
|
|
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();
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exerciseDates = new Date[] { temp_exerciseDate };
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
|
|
}
|
|
}
|
|
|
|
if (koObservationDates == null)
|
|
{
|
|
var monthlyDates = new List<Date>();
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
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<Date, double> dividends = null,
|
|
string knockInOutStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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);
|
|
}
|
|
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
//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<autocall_observation> happenedObservations,
|
|
double dividendRate = 0.0,
|
|
bool isMoneynessOption = false,
|
|
double initialSpotPrice = 0.0,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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);
|
|
}
|
|
}
|
|
|
|
public 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<autocall_observation> happenedObservations,
|
|
bool isMoneynessOption = false,
|
|
double initialSpotPrice = 0.0,
|
|
Dictionary<Date, double> dividends = null,
|
|
string fixings = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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 };
|
|
if (observationDates == null)
|
|
{
|
|
//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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
|
|
#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<Date, double> dividends = null,
|
|
string position = "Buy",
|
|
string engineName = null,
|
|
string discountCurveName = null,
|
|
string barrierStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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);
|
|
}
|
|
}
|
|
|
|
public 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<Date, double> dividends = null,
|
|
string position = "Buy",
|
|
string barrierStatus = "Monitoring",
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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();
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exerciseDates = new Date[] { temp_exerciseDate };
|
|
if (observationDates == null)
|
|
{
|
|
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: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ProtectionRate = principalRate,
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizeFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
|
};
|
|
}
|
|
#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 commodityFuturesPreciseTimeMode = 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,
|
|
commodityFuturesPreciseTimeMode,
|
|
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);
|
|
}
|
|
}
|
|
public 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<Date, double> dividends = null,
|
|
bool hasNightMarket = false,
|
|
bool commodityFuturesPreciseTimeMode = 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: commodityFuturesPreciseTimeMode,
|
|
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 commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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 commodityFuturesPreciseTimeMode,
|
|
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();
|
|
if (observationDates == null)
|
|
{
|
|
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exerciseDates = new Date[] { temp_exerciseDate };
|
|
if (observationDates == null)
|
|
{
|
|
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,
|
|
callMultiplier: callMultiplier,
|
|
putMultiplier: putMultiplier,
|
|
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,
|
|
payCoupon: payCoupon,
|
|
notional: notional,
|
|
settlementGap: null,
|
|
optionPremiumPaymentDate: null,
|
|
optionPremium: 0.0,
|
|
isMoneynessOption: isMoneynessOption,
|
|
initialSpotPrice: initialSpotPrice,
|
|
dividends: null,
|
|
pricingToverride: pricingTOverride,
|
|
hasNightMarket: hasNightMarket,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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]),
|
|
ParticipationRate = participationRate,
|
|
AnnualizedFactor = annualizedFactor,
|
|
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizedFactor)
|
|
};
|
|
}
|
|
#endregion
|
|
|
|
#region 现金流交易
|
|
private static TradeValueResult ValueCashFlowTrade(
|
|
PrebuiltQdpMarketProxy marketProxy,
|
|
string valueDate,
|
|
string startDate,
|
|
string endDate,
|
|
string underlyingCode,
|
|
double notional,
|
|
CashFlowRateTypeEnum rateType,
|
|
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,
|
|
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,
|
|
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.年化利率,
|
|
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
|
|
|
|
/// <summary>
|
|
/// 获取根据rule的补充数据
|
|
/// </summary>
|
|
public static void OptionQuote(ref TradeValueResult valueResult, List<underlying_parameter> quoteRules, double notional, ref QuotationResult qr)
|
|
{
|
|
foreach (var rule in quoteRules)
|
|
{
|
|
OptionQuote(ref valueResult, rule, notional, ref qr);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据规则 调整报价数据
|
|
/// </summary>
|
|
public static double OptionQuote(ref TradeValueResult valueResult, underlying_parameter quoteRule, double notional, ref QuotationResult qr)
|
|
{
|
|
var result = double.NaN;
|
|
if (valueResult != null && quoteRule != null)
|
|
{
|
|
result = valueResult.Pv +
|
|
(quoteRule.Delta ?? 0.0) * (double.IsNaN(valueResult.Delta) ? 0.0 : valueResult.Delta) +
|
|
(quoteRule.Gamma ?? 0.0) * (double.IsNaN(valueResult.Gamma) ? 0.0 : valueResult.Gamma) +
|
|
(quoteRule.Vega ?? 0.0) * (double.IsNaN(valueResult.Vega) ? 0.0 : valueResult.Vega) +
|
|
(quoteRule.Other ?? 0.0);
|
|
}
|
|
else if (valueResult != null && quoteRule == null)
|
|
{
|
|
result = valueResult.Pv;
|
|
}
|
|
|
|
var roundResult = notional == 0 ? OtcFormatHelper.FormatValue(result, 2) : (OtcFormatHelper.FormatValue(result / notional, 2) * notional);
|
|
|
|
//valueResult.TradePrice = result;
|
|
//设置 报价结果
|
|
switch (quoteRule.Type)
|
|
{
|
|
case underlying_parameter.CallAsk:
|
|
qr.CallAsk = Math.Abs(result);
|
|
valueResult.TradePriceAsk = Math.Abs(result);
|
|
valueResult.RoundedTradePriceAsk = Math.Abs(roundResult);
|
|
break;
|
|
case underlying_parameter.CallBid:
|
|
qr.CallBid = Math.Abs(result);
|
|
valueResult.TradePriceBid = Math.Abs(result);
|
|
valueResult.RoundedTradePriceBid = Math.Abs(roundResult);
|
|
break;
|
|
case underlying_parameter.PutAsk:
|
|
qr.PutAsk = Math.Abs(result);
|
|
valueResult.TradePriceAsk = Math.Abs(result);
|
|
valueResult.RoundedTradePriceAsk = Math.Abs(roundResult);
|
|
break;
|
|
case underlying_parameter.PutBid:
|
|
qr.PutBid = Math.Abs(result);
|
|
valueResult.TradePriceBid = Math.Abs(result);
|
|
valueResult.RoundedTradePriceBid = Math.Abs(roundResult);
|
|
break;
|
|
}
|
|
|
|
valueResult.Pv = valueResult.Pv;
|
|
valueResult.RoundedPv = valueResult.RoundedPv;
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取Qdp数据
|
|
/// </summary>
|
|
/// <param name="userId"></param>
|
|
/// <param name="underlying"></param>
|
|
/// <param name="trade"></param>
|
|
/// <param name="spotPrices"></param>
|
|
/// <param name="fixing"></param>
|
|
/// <param name="useTradeVolMode">是否使用TradeVol模式计算</param>
|
|
/// <param name="commodityFuturesPreciseTimeMode"></param>
|
|
/// <param name="correlations"></param>
|
|
/// <param name="discountCurveName"></param>
|
|
/// <param name="userOverrideVol">是否使用用户自定义的波动率</param>
|
|
/// <param name="maturityShift">根据申万的要求:需要支持计算时在实际距离到期日天数的基础上,另外加减天数进行调整</param>
|
|
/// <param name="timeToMaturityDays">用户可以直接输入timeToMaturityDays来指定距离到期日的天数,指定该参数时,maturityShift和指定到期日都是无效的</param>
|
|
/// <returns></returns>
|
|
public static TradeValueResult GetOptionValueResult(
|
|
string userId,
|
|
underlying_manager underlying,
|
|
trade trade,
|
|
double[] spotPrices,
|
|
string fixing = null,
|
|
bool useTradeVolMode = false,
|
|
bool commodityFuturesPreciseTimeMode = false,
|
|
double[] correlations = null,
|
|
string discountCurveName = null,
|
|
int maturityShift = 0,
|
|
double timeToMaturityDays = double.NaN,
|
|
string engineName = null,
|
|
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
|
|
string[] volSurfaceNames = null)
|
|
{
|
|
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);
|
|
|
|
if (volSurfaceNames == null || !volSurfaceNames.Any())
|
|
{
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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<string>(),
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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 "股票":
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
|
|
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
|
|
dividendRateOverride: trade.DividendRate ?? double.NaN,
|
|
pricingRequest: pricingRequest);
|
|
break;
|
|
case "累计期权":
|
|
result = ValueAccumulatorOptionTrade(
|
|
marketProxy,
|
|
valueDate,
|
|
trade.StartDate.Value.ToString("yyyy-MM-dd"),
|
|
endDate,
|
|
trade.ExerciseDate.Value.ToString("yyyy-MM-dd"),
|
|
trade.OptionType,
|
|
underlying.UnderlyingCode,
|
|
trade.UnderlyingInstrumentType,
|
|
trade.Strike ?? 0.0,
|
|
trade.trade_accumulator_option.ObservationDates,
|
|
trade.trade_accumulator_option.Barrier,
|
|
trade.trade_accumulator_option.Coupon,
|
|
trade.trade_accumulator_option.CallMultiplier ?? 1.0,
|
|
trade.trade_accumulator_option.PutMultiplier ?? 1.0,
|
|
trade.trade_accumulator_option.EarlyTerminate,
|
|
trade.trade_accumulator_option.PayCoupon,
|
|
trade.ParticipationRate ?? 1.0,
|
|
spotPrices[0],
|
|
tradeNotional,
|
|
trade.NoRiskRate ?? 0.0,
|
|
trade.DividendRate ?? 0,
|
|
volSurfaceNames[0],
|
|
trade.BuySell,
|
|
trade.IsAnnualized,
|
|
trade.AnnualizeFactor ?? 1.0,
|
|
trade.IsMoneynessOptionData,
|
|
trade.SpotPrice ?? 0,
|
|
discountCurveName: discountCurveName,
|
|
hasNightMarket: variety != null && variety.HasNightMarket,
|
|
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
|
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.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() };
|
|
}
|
|
}
|
|
}
|
|
|
|
public 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 calendar = CalendarImpl.Get("chn");
|
|
var t = double.IsNaN(timeToMaturityDays)
|
|
? TradeCalcHelper.CalculateTTMDays(
|
|
DateTime.Parse(valueDate),
|
|
DateTime.Parse(exerciseDate),
|
|
varietyid: 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);
|
|
}
|
|
|
|
public 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 期货期权卖方交易保证金的收取标准为下列两者中较大者:
|
|
///(1)权利金(期权合约结算价×标的期货合约交易单位)+标的期货合约交易保证金-期权合约虚值额的一半
|
|
///(2)权利金(期权合约结算价×标的期货合约交易单位)+标的期货合约交易保证金的一半
|
|
///看涨虚值额 = max(期权合约执行价格 - 标的期货合约当日结算价,0)*合约乘数;
|
|
///看跌虚值额=max(标的期货合约当日结算价 - 期权合约执行价格,0)*合约乘数。
|
|
///豆粕期权的合约乘数是10、标的期货合约的交易单位是10.
|
|
///所以以上2个公式可以结合成为一个公式:
|
|
///保证金=权利金+MAX(期货保证金-1/2虚值额,1/2期货保证金)
|
|
/// </summary>
|
|
/// <param name="premium">权利金</param>
|
|
/// <param name="amount">交易量</param>
|
|
/// <param name="strike">行权价</param>
|
|
/// <param name="spotPrice">现价</param>
|
|
/// <param name="futureMarginRatio">期货保证金率</param>
|
|
/// <param name="optionType">看涨看跌</param>
|
|
/// <returns></returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 股指期货类型转换为商品期货类型
|
|
/// </summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static string ConvertInstrumentType(string instrumentType)
|
|
{
|
|
return ConsGlobal.InstrumentType.IsStockIF(instrumentType) ? ConsGlobal.InstrumentType.CommodityFutures : instrumentType;
|
|
}
|
|
|
|
public static T CheckMaturityDate<T>(this T trade) where T : TradeBase
|
|
{
|
|
if (trade?.MaturityDate.Year > 2030)
|
|
{
|
|
trade.MaturityDate = new Date(DateTime.Now.AddYears(3));
|
|
}
|
|
return trade;
|
|
}
|
|
}
|
|
}
|