using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity;
using Qdp.ComputeService.Data.CommonModels.ValuationParams.FixedIncome;
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.Products.Rates;
using Qdp.Pricing.Library.Options.Products.Accumulator;
using Qdp.Pricing.Library.Options.Products.Airbag;
using Qdp.Pricing.Library.Options.Products.Asian;
using Qdp.Pricing.Library.Options.Products.AsianSyntheticSpread;
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.Rainbow;
using Qdp.Pricing.Library.Options.Products.RangeAccrual;
using Qdp.Pricing.Library.Options.Products.Spread;
using Qdp.Pricing.Library.Options.Products.SyntheticSpread;
using Qdp.Pricing.Library.Options.Products.Vanilla;
using Qdp.Pricing.Library.Options.Utilities;
using System.Runtime.InteropServices;
using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.Modules.CalculationModule.Abstract;
using YLErp.Modules.TradeModule.Structure_dz;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
using YLErp.ThirdParty.CaculatePrice.DongZheng.Dto;
namespace YLErp.Modules.CalculationModule
{
///
/// 构建QDP交易
///
public class QdpTradeBuilder
{
private static IYcLogger log = LogFactory.GetLogger(nameof(QdpTradeBuilder));
#region ----香草期权----
///
/// 香草期权
///
public static VanillaOptionTrade GetVanillaOptionTrade(VanillaOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, 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 = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var vanillaOption = new VanillaOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: param.optionType,
strike: param.strike,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new VanillaOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: vanillaOption.StartDate,
maturityDate: vanillaOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: vanillaOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve,
param.volSurfaceNames?.FirstOrDefault(), vanillaOption.UnderlyingTickers[0]),
};
}
///
/// 香草期权
///
public static VanillaOptionTrade GetVanillaOptionTrade(OtcTradeBase td, OptionTradeParamRequest request, bool isExchangeOption)
{
var param = GetVanillaOptionTradeParam(td, request, isExchangeOption);
return GetVanillaOptionTrade(param);
}
///
/// 香草期权
///
/// 是否场内期权
public static VanillaOptionTradeParam GetVanillaOptionTradeParam(OtcTradeBase td, OptionTradeParamRequest request, bool isExchangeOption)
{
var result = BuildTradeParamBase(td, request);
if (isExchangeOption)
{
result.buysell = "Buy";
result.isMoneynessOption = false;
}
else if (td.Comments?.Trim() == "YQ")
{
result.riskFreeRate = 0;
result.isForwardTrade = true;
}
return result;
}
#endregion
#region ----合成价差期权----
///
/// 合成价差期权
///
public static SyntheticNormalSpreadOptionTrade GetSSpreadOptionTrade(SSpreadOptionTradeParam param)
{
CheckOptionTradeParam(param);
if (param.underlyingInstrumentType == null)
{
throw new Exception("标的资产类型不能为空");
}
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, 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 = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var syntheticNormalSpreadOption = new SyntheticNormalSpreadOption(
startDate: optionStartDate,
exercise: exercise,
optionType: param.optionType,
strike: param.strike,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
syntheticCoefficients: param.coefficients,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new SyntheticNormalSpreadOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: syntheticNormalSpreadOption.StartDate,
maturityDate: syntheticNormalSpreadOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: syntheticNormalSpreadOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve,
param.volSurfaceNames?.FirstOrDefault(), syntheticNormalSpreadOption.UnderlyingTickers[0]),
};
}
///
/// 合成价差期权
///
public static SyntheticNormalSpreadOptionTrade GetSSpreadOptionTrade(OtcTradeBase td, OptionTradeParamRequest request = null)
{
var param = GetSSpreadOptionTradeParam(td, request);
return GetSSpreadOptionTrade(param);
}
///
/// 合成价差期权
///
public static SSpreadOptionTradeParam GetSSpreadOptionTradeParam(OtcTradeBase td, OptionTradeParamRequest request, double[] coefficients = null)
{
var result = BuildTradeParamBase(td, request);
if (td.Comments?.Trim() == "YQ")
{
result.riskFreeRate = 0;
result.isForwardTrade = true;
}
result.coefficients = coefficients;
return result;
}
#endregion
#region ----亚式期权----
///
/// 亚式期权
///
public static AsianOptionTrade GetAsianOptionTrade(AsianOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
Date[] exerciseDates, 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(param.observationDateStr))
{
observationDates = QdpHelper.ParseObservationDate(param.observationDateStr);
}
else
{
var observationStartDate = param.averagingPeriodStartDate ?? optionStartDate;
var observationEndDate = QdpCalendarHelper.GetNonHolidayDefore(param.originalExerciseDate);
if (PS.Config.ErpElement.AsianOptionObDatesIncludeStartDate)
{
observationDates = calendar.BizDaysBetweenDatesInclEndDay(observationStartDate, observationEndDate).ToArray();
}
else
{
observationDates = calendar.BizDaysBetweenDatesExcluStartDay(observationStartDate, observationEndDate).ToArray();
}
}
var asianType = (AsianType)Enum.Parse(typeof(AsianType), param.payoffType);
if (observationDates.Length <= 1 && asianType != AsianType.EnhancedArithmeticAverage)
{
throw new ServiceException("均价起算日不合理");
}
var fixingValues = QdpHelper.ParseFixingsFromString(param.fixings);
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var asianOption = new AsianOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: (OptionExercise)Enum.Parse(typeof(OptionExercise), param.exerciseType),
optionType: param.optionType,
asianType: asianType,
strikeStyle: (StrikeStyle)Enum.Parse(typeof(StrikeStyle), param.strikeStyle),
strike: param.strike,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
fixings: fixingValues,
strikeGearingFator: param.strikeGearingFactor,
participationRate: param.participationRate,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor,
enhancedPrice: param.enhancedPrice)
{
SettleMode = param.settleMode == "AtEnd" ? SettleMode.AtEnd : SettleMode.AtHit,
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new AsianOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: asianOption.StartDate,
maturityDate: asianOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: asianOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve,
param.volSurfaceNames?.FirstOrDefault(), asianOption.UnderlyingTickers[0]),
};
}
///
/// 亚式期权
///
public static AsianOptionTrade GetAsianOptionTrade(OtcTradeBase td, trade_asian_option asianOption, OptionTradeParamRequest request = null)
{
var param = GetAsianOptionTradeParam(td, asianOption, request);
return GetAsianOptionTrade(param);
}
///
/// 亚式合成价差期权
///
public static AsianSyntheticNormalSpreadOptionTrade GetAsianSSpreadOptionTrade(AsianOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, 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(param.observationDateStr))
{
observationDates = QdpHelper.ParseObservationDate(param.observationDateStr);
}
else
{
var observationStartDate = param.averagingPeriodStartDate ?? optionStartDate;
observationDates = calendar.BizDaysBetweenDatesExcluStartDay(observationStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var fixingValues = QdpHelper.ParseFixingsFromString(param.fixings);
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var asianOption = new AsianSyntheticNormalSpreadOption(
startDate: optionStartDate,
exercise: (OptionExercise)Enum.Parse(typeof(OptionExercise), param.exerciseType),
optionType: param.optionType,
asianType: (AsianType)Enum.Parse(typeof(AsianType), param.payoffType),
strikeStyle: (StrikeStyle)Enum.Parse(typeof(StrikeStyle), param.strikeStyle),
strike: param.strike,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
fixings: fixingValues,
strikeGearingFactor: 1.0,
participationRate: param.participationRate,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
SettleMode = param.settleMode == "AtEnd" ? SettleMode.AtEnd : SettleMode.AtHit,
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new AsianSyntheticNormalSpreadOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: asianOption.StartDate,
maturityDate: asianOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: asianOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve,
param.volSurfaceNames?.FirstOrDefault(), asianOption.UnderlyingTickers[0])
};
}
///
/// 亚式合成价差期权
///
public static AsianSyntheticNormalSpreadOptionTrade GetAsianSSpreadOptionTrade(OtcTradeBase td, trade_asian_option asianOption, OptionTradeParamRequest request = null)
{
var param = GetAsianOptionTradeParam(td, asianOption, request);
return GetAsianSSpreadOptionTrade(param);
}
///
/// 亚式期权
///
/// 标的现价
public static AsianOptionTradeParam GetAsianOptionTradeParam(OtcTradeBase td, trade_asian_option asianOption, OptionTradeParamRequest request)
{
if (asianOption == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的亚式期权交易数据");
}
var result = BuildTradeParamBase(td, request);
result.payoffType = asianOption.PayoffType;
result.settleMode = asianOption.SettleMode;
result.strikeStyle = asianOption.StrikeType;
result.strikeGearingFactor = asianOption.StrikeGearingFactor ?? 1;
result.fixings = request?.fixings;
//在计算的时候判断
//result.fixings = TradeCalcHelper.CheckAsiaFixings(td,asianOption,request.fixings,spotPrice);
result.averagingPeriodStartDate = asianOption.AveragingPeriodStartDate ?? td.TradeDate ?? DateTime.MaxValue;
result.observationDateStr = asianOption.ObservationDates;
result.enhancedPrice = asianOption.EnhancedPrice;
return result;
}
#endregion
#region ----二元期权----
///
/// 二元期权
///
public static BinaryOptionTrade GetBinaryOptionTrade(BinaryOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var rebateType = (string.IsNullOrEmpty(param.binaryRebateType) ? "AtEnd" : param.binaryRebateType).ToBinaryRebateType();
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, observationDates;
exerciseDates = new Date[] { temp_exerciseDate };
observationDates = new Date[] { temp_exerciseDate };
var barrierObservationDates = QdpHelper.ParseObservationDate(param.observationDateStr);
if (barrierObservationDates != null)
{
observationDates = barrierObservationDates;
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var binaryOption = new BinaryOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: param.optionType,
strike: param.strike,
underlyingProductType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
binaryOptionPayoffType: (BinaryOptionPayoffType)Enum.Parse(typeof(BinaryOptionPayoffType), param.payoffType),
cashOrNothingAmount: param.cashOrNothingAmount,
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
upperBarrier: param.upperBarrier,
cashOrNothingAmountHigh: param.cashOrNothingAmountHigh,
isDiscreteMonitored: param.isDiscreteMonitored,
rebateAnnualizedAtKO: param.rebateAnnualizedAtKO,
rebateDayCount: string.IsNullOrWhiteSpace(param.rebateDayCount) ? new Act365() : param.rebateDayCount.ToDayCountImpl(),
dividends: param.dividends,
binaryRebateType: rebateType,
pricingToverride: pricingTOverride,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new BinaryOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: binaryOption.StartDate,
maturityDate: binaryOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: binaryOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), binaryOption.UnderlyingTickers[0]),
};
}
///
/// 二元期权
///
public static BinaryOptionTrade GetBinaryOptionTrade(OtcTradeBase td, trade_binary_option binaryOption, OptionTradeParamRequest request = null)
{
var param = GetBinaryOptionTradeParam(td, binaryOption, request);
return GetBinaryOptionTrade(param);
}
///
/// 二元期权
///
public static BinaryOptionTradeParam GetBinaryOptionTradeParam(OtcTradeBase td, trade_binary_option binaryOption, OptionTradeParamRequest request)
{
if (binaryOption == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的二元期权交易数据");
}
var result = BuildTradeParamBase(td, request);
result.payoffType = binaryOption.PayoffType;
result.upperBarrier = binaryOption.UpperBarrier ?? double.NaN;
if (td.IsUsePremiumRate == true)
{
result.cashOrNothingAmount = (binaryOption.CashOrNothingAmountRate ?? 0) * (td.SpotPrice ?? 0);
result.cashOrNothingAmountHigh = (binaryOption.CashOrNothingAmountHighRate ?? 0) * (td.SpotPrice ?? 0);
}
else
{
result.cashOrNothingAmount = binaryOption.CashOrNothingAmount ?? 0;
result.cashOrNothingAmountHigh = binaryOption.CashOrNothingAmountHigh ?? 0;
}
result.binaryRebateType = binaryOption.RebateType;
result.isDiscreteMonitored = binaryOption.IsDiscreteMonitored;
result.binaryOptionReplicationStrategy = valuedateBLL.BinaryReplicationStrategy;
result.replicationShiftSize = binaryOption.Offset ?? (td.Strike ?? 0) * 0.01;
result.observationDateStr = binaryOption.ObservationDates;
result.rebateDayCount = binaryOption.RebateDayCount;
result.rebateAnnualizedAtKO = binaryOption.RebateAnnualizedAtKO;
return result;
}
#endregion
#region ----障碍期权----
///
/// 障碍期权
///
public static BarrierOptionTrade GetBarrierOptionTrade(BarrierOptionTradeParam param, string valueDateStr = null)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
//这里不容易理解:为了实现在定价界面的“交易日为过去日期和未来日期的交易,应把交易日当天完整计算在内”的逻辑,
//交易日期(通常被往前推了一天)有可能小于开始日,这种情况下应该把开始日设置成和估值日一样,否则从交易日到第一个观察日的时间长度就不准确了
var optionStartDate = param.startDate;
if (valueDateStr != null)
{
var tradeDate = new Date(DateTime.Parse(valueDateStr));
if (tradeDate < param.startDate)
{
optionStartDate = tradeDate;
}
}
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, 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 = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var barrierObservationDates = QdpHelper.ParseObservationDate(param.observationDateStr);
if (barrierObservationDates != null)
{
observationDates = barrierObservationDates;
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var barrierOption = new BarrierOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: param.optionType,
strike: param.strike,
rebate: param.rebate,
coupon: 0.0,
participationRate: param.participationRate,
barrierType: (BarrierType)Enum.Parse(typeof(BarrierType), param.barrierType),
lowerBarrier: param.barrierPrice,
upperBarrier: param.upperBarrierPrice,
isDiscreteMonitored: param.isDiscrete,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
barrierObservationDates: barrierObservationDates,
highRebate: param.rebateHigh,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
position: QdpConverter.ConvertPosition(param.buysell),
barrierShift: param.barrierShift,
rebateAnnualizedAtKO: param.rebateAnnualizedAtKO,
rebateDayCount: string.IsNullOrWhiteSpace(param.rebateDayCount) ? new Act365() : param.rebateDayCount.ToDayCountImpl(),
pricingToverride: pricingTOverride,
rebateType: (string.IsNullOrEmpty(param.rebateType) ? "AtEnd" : param.rebateType).ToBinaryRebateType(),
barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus),
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new BarrierOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: barrierOption.StartDate,
maturityDate: barrierOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: barrierOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), barrierOption.UnderlyingTickers[0]),
};
}
///
/// 障碍期权
///
public static BarrierOptionTrade GetBarrierOptionTrade(OtcTradeBase td, trade_barrier_option barrierOption, OptionTradeParamRequest request = null)
{
var param = GetBarrierOptionTradeParam(td, barrierOption, request);
return GetBarrierOptionTrade(param);
}
///
/// 障碍期权
///
public static BarrierOptionTradeParam GetBarrierOptionTradeParam(OtcTradeBase td, trade_barrier_option barrierOption, OptionTradeParamRequest request)
{
if (barrierOption == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的障碍期权交易数据");
}
var result = BuildTradeParamBase(td, request);
result.barrierType = barrierOption.BarrierTypeEn;
result.barrierPrice = barrierOption.BarrierPrice ?? 0;
result.upperBarrierPrice = barrierOption.UpperBarrierPrice ?? 0;
result.isDiscrete = barrierOption.IsDiscrete;
result.observationDateStr = barrierOption.ObservationDates;
if (td.IsUsePremiumRate == true)
{
result.rebate = (barrierOption.RebateRate ?? 0) * (td.SpotPrice ?? 0);
}
else
{
result.rebate = barrierOption.Rebate ?? 0;
}
if (td.IsUsePremiumRate == true)
{
result.rebateHigh = (barrierOption.RebateHighRate ?? double.NaN) * (td.SpotPrice ?? 0);
}
else
{
result.rebateHigh = barrierOption.RebateHigh ?? double.NaN;
}
result.rebateType = barrierOption.RebateType;
result.barrierShift = barrierOption.BarrierShift ?? 0.0;
result.barrierStatus = barrierOption.KnockInOutStatus;
result.rebateAnnualizedAtKO = barrierOption.RebateAnnualizedAtKO;
result.rebateDayCount = barrierOption.RebateDayCount;
//if (result.rebateAnnualizedAtKO)
//{
// var dayCount = string.IsNullOrWhiteSpace(result.rebateDayCount) ? new Act365() : result.rebateDayCount.ToDayCountImpl();
// result.rebate /= dayCount.CalcDayCountFraction(result.startDate, result.endDate);
//}
return result;
}
#endregion
#region ----双鲨期权----
///
/// 双鲨期权
///
public static DoubleSharkFinOptionTrade GetDoubleSharkFinOptionTrade(DoubleSharkFinOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
var observationDates = QdpHelper.ParseObservationDate(param.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 = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var lowRebateValue = double.IsNaN(param.rebate) ? 0.0 : param.rebate;
var highReateValue = double.IsNaN(param.rebateHigh) ? 0.0 : param.rebateHigh;
var doubleSharkFinOption = new DoubleSharkFinOption(
strikeHigh: param.strikeHigh,
strikeLow: param.strikeLow,
barrierHigh: Enumerable.Repeat(param.barrierHigh, observationDates.Length).ToArray(),
barrierLow: Enumerable.Repeat(param.barrierLow, observationDates.Length).ToArray(),
rebateLow: Enumerable.Repeat(lowRebateValue, observationDates.Length).ToArray(),
rebateHigh: Enumerable.Repeat(highReateValue, observationDates.Length).ToArray(),
rebateType: (string.IsNullOrEmpty(param.rebateType) ? "AtEnd" : param.rebateType).ToBinaryRebateType(),
observationDates: observationDates,
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
participationRate: param.participationRate,
isDiscreteMonitored: param.isDiscrete,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
callParticipationRate: param.callParticipationRate,
putParticipationRate: param.putParticipationRate,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0,
pricingToverride: pricingTOverride,
barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus),
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new DoubleSharkFinOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: doubleSharkFinOption.StartDate,
maturityDate: doubleSharkFinOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: doubleSharkFinOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), doubleSharkFinOption.UnderlyingTickers[0]),
};
}
///
/// 双鲨期权
///
public static DoubleSharkFinOptionTrade GetDoubleSharkFinOptionTrade(OtcTradeBase td, trade_double_sharkfin_option dbsharkOption, OptionTradeParamRequest request = null)
{
var param = GetDoubleSharkFinOptionTradeParam(td, dbsharkOption, request);
return GetDoubleSharkFinOptionTrade(param);
}
///
/// 双鲨期权
///
public static DoubleSharkFinOptionTradeParam GetDoubleSharkFinOptionTradeParam(OtcTradeBase td, trade_double_sharkfin_option dbsharkOption, OptionTradeParamRequest request)
{
if (dbsharkOption == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的双鲨期权交易数据");
}
var result = BuildTradeParamBase(td, request);
result.strikeLow = td.Strike ?? 0.0;
result.strikeHigh = dbsharkOption.StrikeHigh ?? td.Strike ?? 0.0;
result.barrierHigh = dbsharkOption.BarrierHigh;
result.barrierLow = dbsharkOption.BarrierLow;
if (td.IsUsePremiumRate == true)
{
result.rebate = (dbsharkOption.RebateRate ?? 0) * (td.SpotPrice ?? 0);
result.rebateHigh = (dbsharkOption.RebateHighRate ?? 0) * (td.SpotPrice ?? 0);
}
else
{
result.rebate = dbsharkOption.Rebate ?? 0;
result.rebateHigh = dbsharkOption.RebateHigh ?? 0;
}
result.rebateType = dbsharkOption.RebateType;
result.isDiscrete = dbsharkOption.IsDiscrete;
result.observationDateStr = dbsharkOption.ObservationDates;
result.callParticipationRate = dbsharkOption.CallParticipationRate ?? 1.0;
result.putParticipationRate = dbsharkOption.PutParticipationRate ?? 1.0;
result.barrierStatus = dbsharkOption.KnockInOutStatus;
return result;
}
#endregion
#region ----雪球期权----
///
/// 雪球期权
///
/// 构建参数
/// 雪球期权类型
///
public static SnowballTrade GetSnowballOptionTrade(SnowballOptionTradeParam param
, SnowBallOptionMode snowballMold = SnowBallOptionMode.Auto)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
var observationDates = QdpHelper.ParseObservationDate(param.observationDateStr);
//敲出观察
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(param.koObservationDateStr);
var koObservationDates = customizedResults.Item1;
var customizedKOBarriers = customizedResults.Item2;
var customizedCoupons = customizedResults.Item3;
Date[] exerciseDates;
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();
}
}
koObservationDates ??= QdpObservationHelper.GetDefaultKoObservationDatesForSnowbalV2(optionStartDate, temp_exerciseDate);
//票息日历规则
if (string.IsNullOrWhiteSpace(param.couponDayCount))
{
param.couponDayCount = "Act365";
}
//票息支付日期
var couponPaymentDates = QdpHelper.ParseObservationDate(param.couponPaymentDateStr);
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingToverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
SimpleSnowball snowball;
if (snowballMold == SnowBallOptionMode.AnnualizedPremiumSnowball
|| snowballMold == SnowBallOptionMode.Auto && param.annualizedPremiumRate > 0)
{
snowball = new AnnualizedPremiumSnowball(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
koBarrier: param.koBarrier,
kiBarrier: double.NaN,
coupon: param.coupon,
optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0
koObsDates: koObservationDates,
kiObsDates: observationDates,
observationDates: null,
notional: param.notional,
initialSpotPrice: param.initialSpotPrice,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
exercise: OptionExercise.European,
isMoneynessOption: param.isMoneynessOption,
optionType: param.optionType,
barrierType: param.optionType == OptionType.Put ? BarrierType.DownAndOut : BarrierType.UpAndOut,
strike: 0,
spreadStrikeAtKO: param.spreadStrikeAtKO,
spreadStrikeAtMaturity: param.spreadStrikeAtMaturity,
fixedCoupon: param.isFixedCoupon,
kORebate: param.koRebate,
kOBarrierAdjustStep: param.koBarrierAdjustStep,
useOptionPayoffAtKO: param.useOptionPayoffAtKO,
useOptionPayoffAtMaturity: false,
annualizedOptionPayoff: param.annualizedOptionPayoff,
customizedKOBarriers: customizedKOBarriers,
customizedCoupons: customizedCoupons,
pricingToverride: pricingToverride,
barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus),
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
annualizedFactor: param.annualizedFactor,
koStrike: param.koStrike,
couponDayCount: param.couponDayCount.ToDayCountImpl(),
koOptionType: param.koOptionType,
kiOptionType: param.kiOptionType,
couponPaymentDates: couponPaymentDates);
((AnnualizedPremiumSnowball)snowball).AnnualizedPremium = param.annualizedPremiumRate ?? 0;
}
else
{
snowball = new SimpleSnowball(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
koBarrier: param.koBarrier,
kiBarrier: param.kiBarrier,
coupon: param.coupon,
optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0
koObsDates: koObservationDates,
kiObsDates: observationDates,
observationDates: null,
notional: param.notional,
initialSpotPrice: param.initialSpotPrice,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
exercise: OptionExercise.European,
isMoneynessOption: param.isMoneynessOption,
optionType: param.optionType,
barrierType: param.optionType == OptionType.Put ? BarrierType.DownAndOut : BarrierType.UpAndOut,
strike: param.strike,
spreadStrikeAtKO: param.spreadStrikeAtKO,
spreadStrikeAtMaturity: param.spreadStrikeAtMaturity,
fixedCoupon: param.isFixedCoupon,
kORebate: param.koRebate,
kOBarrierAdjustStep: param.koBarrierAdjustStep,
useOptionPayoffAtKO: param.useOptionPayoffAtKO,
useOptionPayoffAtMaturity: param.useOptionPayoffAtMaturity,
annualizedOptionPayoff: param.annualizedOptionPayoff,
customizedKOBarriers: customizedKOBarriers,
customizedCoupons: customizedCoupons,
pricingToverride: pricingToverride,
barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus),
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
annualizedFactor: param.annualizedFactor,
koStrike: param.koStrike,
couponDayCount: param.couponDayCount.ToDayCountImpl(),
koOptionType: param.koOptionType,
kiOptionType: param.kiOptionType,
couponPaymentDates: couponPaymentDates);
}
snowball.UnderlyingTickers = param.underlyingTickers;
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new SnowballTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: snowball.StartDate,
maturityDate: snowball.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: snowball)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), snowball.UnderlyingTickers[0]),
};
}
///
/// 雪球期权
///
public static SnowballTrade GetSnowballOptionTrade(OtcTradeBase td, trade_snowball snowball,
OptionTradeParamRequest request = null, SnowBallOptionMode snowballMold = SnowBallOptionMode.Auto)
{
var tradeParam = GetSnowballTradeParam(td, snowball, request);
return GetSnowballOptionTrade(tradeParam, snowballMold);
}
///
/// 雪球期权
///
public static SnowballOptionTradeParam GetSnowballTradeParam(OtcTradeBase td, trade_snowball snowball, OptionTradeParamRequest request)
{
if (snowball is null)
{
throw new ArgumentNullException(nameof(snowball));
}
var result = BuildTradeParamBase(td, request);
//敲出设置
result.koBarrier = snowball.KOBarrier;
result.koObservationDateStr = snowball.KOObservationDates;
result.couponPaymentDateStr = snowball.CouponUsePaymentDate == true ? snowball.KOObservationSettleDates : null;
result.koRebate = snowball.KORebate;
result.isFixedCoupon = snowball.IsFixedCoupon;
result.couponDayCount = snowball.CouponDayCount;
result.useOptionPayoffAtKO = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate;//敲出转期权
result.koOptionType = QdpConverter.ConvertAutocallKOOptionType(snowball.KOPayoffType, QdpConverter.ConvertCallPut(td.CallPut));
result.kiOptionType = QdpConverter.ConvertOptionType(snowball.KIPayoffType);
result.koStrike = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate ? (snowball.SpreadStrikeAtKO1 ?? -1) : double.NaN;
result.spreadStrikeAtKO = snowball.SpreadStrikeAtKO ?? -1;
result.koBarrierAdjustStep = snowball.KOBarrierAdjustStep;
//敲入设置
result.kiBarrier = snowball.KIBarrier;
result.observationDateStr = snowball.ObservationDates;
result.useOptionPayoffAtMaturity = snowball.KIPayoffType != KIPayoffTypeEnum.None;//未敲出转期权
result.strike = snowball.SpreadStrikeAtMaturity1 ?? -1;
result.spreadStrikeAtMaturity = snowball.SpreadStrikeAtMaturity ?? -1;
//红利票息
result.coupon = snowball.Coupon > 0 ? snowball.Coupon : 0;
//保本雪球
result.annualizedPremiumRate = snowball.AnnualizedPremiumRate;
//其他
result.barrierStatus = snowball.KnockInOutStatus;
//使用自身的年化系数设置
result.annualizedOptionPayoff = snowball.IsAnnualized2;
result.annualizedFactor = snowball.AnnualizeFactor2 ?? 1.0;
return result;
}
///
/// 雪球期权
///
public static SnowballSpecialistOptionTradeParam GetSnowballSpecialistTradeParam(OtcTradeBase td, trade_snowball snowball, OptionTradeParamRequest request)
{
if (snowball is null)
{
throw new ArgumentNullException(nameof(snowball));
}
var result = BuildTradeParamBase(td, request);
//敲出设置
result.koBarrier = snowball.KOBarrier;
result.koObservationDateStr = snowball.KOObservationDates;
result.couponPaymentDateStr = snowball.CouponUsePaymentDate == true ? snowball.KOObservationSettleDates : null;
result.koRebate = snowball.KORebate;
result.isFixedCoupon = snowball.IsFixedCoupon;
result.couponDayCount = snowball.CouponDayCount;
result.useOptionPayoffAtKO = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate;//敲出转期权
result.koOptionType = QdpConverter.ConvertAutocallKOOptionType(snowball.KOPayoffType, QdpConverter.ConvertCallPut(td.CallPut));
result.kiOptionType = QdpConverter.ConvertOptionType(snowball.KIPayoffType);
result.koStrike = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate ? (snowball.SpreadStrikeAtKO1 ?? -1) : double.NaN;
result.spreadStrikeAtKO = snowball.SpreadStrikeAtKO ?? -1;
result.koBarrierAdjustStep = snowball.KOBarrierAdjustStep;
//敲入设置
result.kiBarrier = snowball.KIBarrier;
result.observationDateStr = snowball.ObservationDates;
result.useOptionPayoffAtMaturity = snowball.KIPayoffType != KIPayoffTypeEnum.None;//未敲出转期权
result.strike = snowball.SpreadStrikeAtMaturity1 ?? -1;
result.spreadStrikeAtMaturity = snowball.SpreadStrikeAtMaturity ?? -1;
//红利票息
result.coupon = snowball.Coupon > 0 ? snowball.Coupon : 0;
//保本雪球
result.annualizedPremiumRate = snowball.AnnualizedPremiumRate;
//其他
result.barrierStatus = snowball.KnockInOutStatus;
//使用自身的年化系数设置
result.annualizedOptionPayoff = snowball.IsAnnualized2;
result.annualizedFactor = snowball.AnnualizeFactor2 ?? 1.0;
//预付金是否参与定价
result.PrepaymentUsed = snowball.PrepaymentUsed;
//预付金比例
result.PrepaymentRatio = snowball.PrepaymentRatio ?? 0;
//预付金返息率
result.PrepaymentInterestRate = snowball.PrepaymentInterestRate ?? 0;
//预付金折现率
result.PrepaymentConvertCashRate = snowball?.PrepaymentConvertCashRate ?? 0;
//终日是否计息
result.CouponIncludeEndDate = snowball.CouponIncludeEndDate ?? false;
//增强参与率
result.EnhancedParticipationRate = snowball.EnhancedParticipationRate ?? 0;
// 敲入参与率
result.KIParticipationRate = snowball.KIParticipationRate ?? 0;
// 保本比率
result.PrincipalProtectionRate = snowball.PrincipalProtectionRate ?? 0;
return result;
}
#endregion
#region ----凤凰期权----
///
/// 凤凰期权
///
public static AutocallTrade GetAutocallOptionTrade(AutocallOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates = null;
var observationDates = QdpHelper.ParseObservationDate(param.observationDateStr);
var customizedResults = QdpHelper.ParseAutocallCustomizedInfoV2(param.koObservationDateStr, param.callput, param.initialSpotPrice, param.isMoneynessOption);
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)
{
koObservationDates = QdpObservationHelper.GetDatesWithFixedTerm(optionStartDate, temp_exerciseDate, "1M", bdc: BusinessDayConvention.Following, alignEnd: true)
.Select(n => (Date)n).ToArray();
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var autocall = new AutoCall(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
koBarrier: param.koBarrier,
kiBarrier: param.kiBarrier,
couponBarrier: param.couponBarrier,
coupon: param.coupon,
couponPayAtMaturity: param.couponPayAtMaturity,
includeCouponAfterKI: param.includeCouponAfterKI,
optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0
koObsDates: koObservationDates,
kiObsDates: observationDates,
observationDates: null,
notional: param.notional,
initialSpotPrice: param.initialSpotPrice,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
couponDayCount: (param.couponDayCount.TrimToNull() ?? "Act365").ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
barrierType: param.optionType == OptionType.Put ? BarrierType.DownAndOut : BarrierType.UpAndOut,//20220415
strike: param.strike,
spreadStrike: param.spreadStrike,
fixedCoupon: param.isFixedCoupon,
annualizedOptionPayoff: param.annualizedOptionPayoff,
kiOptionType: param.kiOptionType.ToOptionType(),
customizedKOBarriers: customizedKOBarriers,
customizedCoupons: customizedCoupons,
isMoneynessOption: param.isMoneynessOption,
happenedObservations: param.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(),
pricingToverride: pricingTOverride,
barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus),
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new AutocallTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: autocall.StartDate,
maturityDate: autocall.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,//autocall.Notional
price: 0.0,
option: autocall)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), autocall.UnderlyingTickers[0]),
};
}
///
/// 凤凰期权
///
public static AutocallTrade GetAutocallOptionTrade(OtcTradeBase td, trade_autocall autocall, OptionTradeParamRequest request = null)
{
var tradeParam = GetAutocallOptionTradeParam(td, autocall, request);
return GetAutocallOptionTrade(tradeParam);
}
///
/// 凤凰期权
///
public static AutocallOptionTradeParam GetAutocallOptionTradeParam(OtcTradeBase td, trade_autocall autocall, OptionTradeParamRequest request)
{
if (autocall == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的凤凰期权交易数据");
}
var result = BuildTradeParamBase(td, request);
result.coupon = autocall.Coupon;
result.isFixedCoupon = autocall.IsFixedCoupon;
result.couponDayCount = autocall.CouponDayCount;
result.kiOptionType = QdpConverter.ConvertOptionType(autocall.KIPayoffType).ToString();
result.couponBarrier = autocall.CouponBarrier;
result.couponPayAtMaturity = autocall.CouponPayType != CouponPayTypeEnum.AtCreated;
result.koBarrier = autocall.KOBarrier;
result.koObservationDateStr = autocall.KOObservationDates;
result.kiBarrier = autocall.KIBarrier;
result.observationDateStr = autocall.ObservationDates;
result.includeCouponAfterKI = autocall.IncludeCouponAfterKI;
//对于凤凰期权,td.Strike应该等于敲入行权价,特别情况下会出现不符合的情况
result.strike = autocall.SpreadStrike1 ?? -1;
result.spreadStrike = autocall.SpreadStrike ?? -1;
result.happenedObservations = autocall.HappenedObservations;
result.barrierStatus = autocall.KnockInOutStatus;
//使用自己的年化系数配置
result.annualizedOptionPayoff = autocall.IsAnnualized2;
result.annualizedFactor = autocall.AnnualizeFactor2 ?? 1.0;
return result;
}
#endregion
#region ----收益增强----
///
/// 收益增强
///
public static UnderlyingPayoffEnhanceTrade GetUnderlyingEnhanceTrade(UnderlyingEnhanceTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
throw new Exception("到期日期不能小于起始日");
}
Date[] exerciseDates, 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 = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var underlyingEnhance = new UnderlyingPayoffEnhance(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: param.optionType,
strike: param.strike,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
enhancePayoffDayCount: new Act365(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
annualizedEnhanceRate: param.annualizedEnhanceRate,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new UnderlyingPayoffEnhanceTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: underlyingEnhance.StartDate,
maturityDate: underlyingEnhance.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: underlyingEnhance)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), underlyingEnhance.UnderlyingTickers[0])
};
}
///
/// 收益增强
///
public static UnderlyingPayoffEnhanceTrade GetUnderlyingEnhanceTrade(OtcTradeBase td, trade_underlying_enhance underlyingEnhance, OptionTradeParamRequest request = null)
{
var tradeParam = GetUnderlyingEnhanceTradeParam(td, underlyingEnhance, request);
return GetUnderlyingEnhanceTrade(tradeParam);
}
///
/// 收益增强
///
public static UnderlyingEnhanceTradeParam GetUnderlyingEnhanceTradeParam(OtcTradeBase td, trade_underlying_enhance underlyingEnhance, OptionTradeParamRequest request)
{
if (underlyingEnhance == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的收益增强结构交易数据");
}
var result = BuildTradeParamBase(td, request);
result.annualizedEnhanceRate = underlyingEnhance.AnnualizedEnhanceRate ?? 0;
return result;
}
#endregion
#region ----气囊结构----
///
/// 气囊结构
///
public static AirbagTrade GetAirbagOptionTrade(AirBagOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, 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 = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var airbag = new Airbag(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: param.optionType,
lowStrike: param.strike,
highStrike: param.highStrike,
barrier: param.barrier,
participationRate: param.participationRate,
kiParticipationRate: param.kiParticipationRate,
isDiscreteMonitored: param.isDiscrete,
hasPayoffLimit: param.hasPayoffLimit,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus),
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new AirbagTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: airbag.StartDate,
maturityDate: airbag.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: airbag)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), airbag.UnderlyingTickers[0]),
};
}
///
/// 气囊结构
///
public static AirbagTrade GetAirbagOptionTrade(OtcTradeBase td, trade_airbag airbag, OptionTradeParamRequest request = null)
{
var param = GetAirbagOptionTradeParam(td, airbag, request);
return GetAirbagOptionTrade(param);
}
///
/// 气囊结构
///
public static AirBagOptionTradeParam GetAirbagOptionTradeParam(OtcTradeBase td, trade_airbag airbag, OptionTradeParamRequest request)
{
if (airbag == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的气囊结构交易数据");
}
var result = BuildTradeParamBase(td, request);
result.barrier = airbag.Barrier;
result.highStrike = airbag.HighStrike;
result.kiParticipationRate = airbag.KIParticipationRate;
result.hasPayoffLimit = airbag.HasPayoffLimit;
result.isDiscrete = airbag.IsDiscreteMonitored;
result.observationDateStr = null;
result.barrierStatus = airbag.KnockInOutStatus;
return result;
}
#endregion
#region ----区间累积----
///
/// 区间累积
///
public static RangeAccrualTrade GetRangeAccrualTrade(RangeAccrualTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
throw new Exception("到期日期不能小于起始日");
}
Date[] exerciseDates = null;
var observationDates = QdpHelper.ParseObservationDate(param.observationDateStr);
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
if (observationDates == null)
{
observationDates = CalendarImpl.Get("chn"). BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
}
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(param.timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var fixingValues = QdpHelper.ParseFixingsFromString(param.fixings);
var bonusRateSettlementDate = param.settlementDate ?? temp_exerciseDate;
if (bonusRateSettlementDate < optionStartDate)
{
bonusRateSettlementDate = temp_exerciseDate;
}
var rangeAccrual = new RangeAccrual(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: param.optionType,
strike: param.strike,
ranges: new RangeDefinition[]
{
new RangeDefinition(param.lowerRange,param.upperRange,param.bonusRate, bonusRateSettlementDate, observationDates)
},
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
fixings: fixingValues,
happenedObservations: param.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: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new RangeAccrualTrade(
tradeId: param.tradeId,
tradeDate: param.startDate,
startDate: rangeAccrual.StartDate,
maturityDate: rangeAccrual.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: rangeAccrual.Notional, price: 0.0, option: rangeAccrual)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), rangeAccrual.UnderlyingTickers[0]),
};
}
///
/// 区间累积
///
public static RangeAccrualTrade GetRangeAccrualTrade(OtcTradeBase td, trade_rangeaccrual rangeaccrual, OptionTradeParamRequest request = null)
{
var tradeParam = GetRangeAccrualTradeParam(td, rangeaccrual, request);
return GetRangeAccrualTrade(tradeParam);
}
///
/// 区间累积
///
public static RangeAccrualTradeParam GetRangeAccrualTradeParam(OtcTradeBase td, trade_rangeaccrual rangeaccrual, OptionTradeParamRequest request)
{
if (rangeaccrual == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的区间累积交易数据");
}
var result = BuildTradeParamBase(td, request);
result.lowerRange = rangeaccrual.LowerRange;
result.upperRange = rangeaccrual.UpperRange;
//注意:在QDP中未处理百分比形式的上下限价格(2021-10-21)
if (result.isMoneynessOption)
{
result.isMoneynessOption = false;
result.strike *= result.initialSpotPrice;
result.lowerRange *= result.initialSpotPrice;
result.upperRange *= result.initialSpotPrice;
}
result.bonusRate = rangeaccrual.BonusRate;
result.observationDateStr = rangeaccrual.ObservationDates;
result.happenedObservations = rangeaccrual.HappenedObservations;
result.fixings = request != null ? request.fixings : "";
return result;
}
#endregion
#region ----彩虹期权----
///
/// 彩虹期权
///
public static RainbowOptionTrade GetRainbowOptionTrade(RainbowOptionTradeParam param)
{
CheckOptionTradeParam(param);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
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(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: (OptionExercise)Enum.Parse(typeof(OptionExercise), param.exerciseType),
optionType: param.optionType,
rainbowType: (RainbowType)Enum.Parse(typeof(RainbowType), param.rainbowType),
strikes: param.strikes,
cashAmount: param.cashAmount,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
underlyingTickers: param.underlyingTickers,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new RainbowOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: rainbowOption.StartDate,
maturityDate: rainbowOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: rainbowOption)
{
ValuationParameters = new OptionValuationParameters(
discountCurveName: MarketProxy.RiskFreeDiscountCurve,
dividendCurveNames: new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve },
volSurfNames: param.volSurfaceNames,
corrSurfNames: new string[] { param.underlyingTickers[0] + "_" + param.underlyingTickers[1] },
underlyingId: null),
};
}
///
/// 彩虹期权
///
public static RainbowOptionTrade GetRainbowOptionTrade(OtcTradeBase td, trade_rainbow_option rainbowOption, OptionTradeParamRequest request = null)
{
var param = GetRainbowOptionTradeParam(td, rainbowOption, request);
return GetRainbowOptionTrade(param);
}
///
/// 彩虹期权
///
public static RainbowOptionTradeParam GetRainbowOptionTradeParam(OtcTradeBase td, trade_rainbow_option rainbowOption, OptionTradeParamRequest request)
{
if (rainbowOption == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的彩虹期权交易数据");
}
var result = BuildTradeParamBase(td, request);
result.underlyingTickers = rainbowOption.UnderlyingCodes;
result.strikes = rainbowOption.Strikes;
result.rainbowType = rainbowOption.RainbowType;
result.cashAmount = rainbowOption.CashAmount ?? 0;
result.correlation = rainbowOption.CorRelation ?? 0;
return result;
}
#endregion
#region ----价差期权----
///
/// 价差期权
///
public static SpreadOptionTrade GetSpreadOptionTrade(SpreadOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
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 < param.weights.Length; i++)
{
normalizedWeight[i] = param.weights[i];
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var spreadOption = new SpreadOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: param.optionType,
spreadType: param.spreadType,
weights: normalizedWeight,
strike: param.strike,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
underlyingTickers: param.underlyingTickers,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor,
riskFreeRateOverride: param.riskFreeRate);
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new SpreadOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: spreadOption.StartDate,
maturityDate: spreadOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: spreadOption)
{
ValuationParameters = GetParametersForSpreadOption(param.underlyingTickers, MarketProxy.RiskFreeDiscountCurve, param.volSurfaceNames)
};
}
///
/// 价差期权
///
public static SpreadOptionTrade GetSpreadOptionTrade(OtcTradeBase td, trade_spread_option spreadOption, OptionTradeParamRequest request, double[] correlations)
{
var param = GetSpreadOptionTradeParam(td, spreadOption, request, correlations);
return GetSpreadOptionTrade(param);
}
public static OptionValuationParameters GetParametersForSpreadOption(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);
}
///
/// 价差期权
///
public static SpreadOptionTradeParam GetSpreadOptionTradeParam(OtcTradeBase td, trade_spread_option spreadOption, OptionTradeParamRequest request, double[] correlations)
{
if (spreadOption == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的价差期权交易数据");
}
var result = BuildTradeParamBase(td, request);
result.underlyingTickers = spreadOption.UnderlyingAssetCodes();
var payoff = new Model.SpreadOptionPayoff(spreadOption.Payoff);
result.weights = payoff.Weights;
result.spreadType = payoff.SpreadType;
result.correlations = correlations;
return result;
}
#endregion
#region ----累计期权----
public static AccumulatorOptionTrade GetAccumulatorOptionTrade(AccumulatorOptionTradeParam param)
{
if (param is SegmentedAccumulatorOptionTradeParam tradeParam)
{
return GetSegmentedAccumulatorOptionTrade(tradeParam);
}
else
{
return GetStandardAccumulatorOptionTrade(param);
}
}
private static AccumulatorOptionTrade GetStandardAccumulatorOptionTrade(AccumulatorOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, 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 = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(param.KOObservationDates);
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(param.Barrier, koObservationDates.Length).ToArray();
}
else if (param.isMoneynessOptionOrigianl)
{
customizedKOBarriers = customizedKOBarriers.Select(n => n * param.initialSpotPrice).ToArray();
}
//if (customizedCoupons == null || customizedCoupons.Length == 0)
//{
// customizedCoupons = Enumerable.Repeat(param.Coupon, koObservationDates.Length).ToArray();
//}
//票息支付日期
var couponPaymentDates = QdpHelper.ParseObservationDate(param.couponPaymentDateStr);
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var settleMode = AccumuSettleMode.CashAtHit;
switch (param.SettlementMode)
{
case "现金当日":
case "现金实物合并结算":
settleMode = AccumuSettleMode.CashAtHit;
break;
case "现金期末":
settleMode = AccumuSettleMode.CashAtEnd;
break;
case "实物交割":
settleMode = AccumuSettleMode.ToFoward;
break;
}
var happenedObservations = settleMode == AccumuSettleMode.CashAtEnd ? param.happenedObservations?.Where(o => o != null).Select(o => new AccumuObservationSettleData
{
SettleDate = new Date(o.EndDate),
SettleNotional = o.PaymentAmount
}).ToList() : null;
var accumulatorOption = new AccumulatorOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
optionType: param.optionType,
strike: param.strike,
barriers: customizedKOBarriers,
callMultiplier: param.CallMultiplier,
putMultiplier: param.PutMultiplier,
earlyTerminate: param.EarlyTerminate,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: koObservationDates,
coupon: param.Coupon,
payCoupon: param.PayCoupon,
couponPaymentDates: couponPaymentDates,
couponDayCount: param.couponDayCount.ToDayCountImpl(),
fixedCoupon: param.isFixedCoupon,
includeStartDateCoupon: param.includeStartDateCoupon,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
pricingToverride: pricingTOverride,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor,
accumuType: param.AccumuType == "子弹" ? AccumuType.bullet : AccumuType.none,
settleMode: settleMode,
happenedObservations: happenedObservations)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new AccumulatorOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: accumulatorOption.StartDate,
maturityDate: accumulatorOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: accumulatorOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), accumulatorOption.UnderlyingTickers[0]),
};
}
private static SegmentedAccumulatorOptionTrade GetSegmentedAccumulatorOptionTrade(SegmentedAccumulatorOptionTradeParam param)
{
CheckOptionTradeParam(param);
var exercise = QdpConverter.ConvertExerciseType(param.exerciseType);
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var temp_exerciseDate = param.exerciseDate;
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates, 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 = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(param.KOObservationDates);
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(param.Barrier, koObservationDates.Length).ToArray();
}
else if (param.isMoneynessOptionOrigianl)
{
customizedKOBarriers = customizedKOBarriers.Select(n => n * param.initialSpotPrice).ToArray();
}
//if (customizedCoupons == null || customizedCoupons.Length == 0)
//{
// customizedCoupons = Enumerable.Repeat(param.Coupon, koObservationDates.Length).ToArray();
//}
//票息支付日期
var couponPaymentDates = QdpHelper.ParseObservationDate(param.couponPaymentDateStr);
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var settleMode = AccumuSettleMode.CashAtHit;
switch (param.SettlementMode)
{
case "现金当日":
settleMode = AccumuSettleMode.CashAtHit;
break;
case "现金期末":
settleMode = AccumuSettleMode.CashAtEnd;
break;
case "实物交割":
settleMode = AccumuSettleMode.ToFoward;
break;
}
var settleMode2 = AccumuSettleMode.CashAtHit;
switch (param.SettleMode2)
{
case "现金当日":
settleMode2 = AccumuSettleMode.CashAtHit;
break;
case "实物交割":
settleMode2 = AccumuSettleMode.ToFoward;
break;
}
var settleMode3 = AccumuSettleMode.CashAtHit;
switch (param.SettleMode3)
{
case "现金当日":
settleMode3 = AccumuSettleMode.CashAtHit;
break;
case "实物交割":
settleMode3 = AccumuSettleMode.ToFoward;
break;
}
var happenedObservations = settleMode == AccumuSettleMode.CashAtEnd ? param.happenedObservations?.Where(o => o != null).Select(o => new AccumuObservationSettleData
{
SettleDate = new Date(o.EndDate),
SettleNotional = o.PaymentAmount
}).ToList() : null;
var accumulatorOption = new SegmentedAccumulatorOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
optionType: param.optionType,
strike: param.strike,
barriers: customizedKOBarriers,
multiplier2: param.Multiplier2,
multiplier1: param.Multiplier1,
earlyTerminate: param.EarlyTerminate,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: koObservationDates,
coupon: param.Coupon,
payCoupon: param.PayCoupon,
couponPaymentDates: couponPaymentDates,
couponDayCount: param.couponDayCount.ToDayCountImpl(),
fixedCoupon: param.isFixedCoupon,
includeStartDateCoupon: param.includeStartDateCoupon,
notional: param.notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: param.isMoneynessOption,
initialSpotPrice: param.initialSpotPrice,
dividends: param.dividends,
pricingToverride: pricingTOverride,
hasNightMarket: param.hasNightMarket,
commodityFuturesPreciseTimeMode: param.preciseTimeMode,
riskFreeRateOverride: param.riskFreeRate,
dividendRateOverride: param.dividendRate,
participationRate: param.participationRate,
isAnnualized: param.isAnnualized,
annualizedFactor: param.annualizedFactor,
accumuType: param.AccumuType == "子弹" ? AccumuType.bullet : AccumuType.none,
settleMode: settleMode,
settleMode2: settleMode2,
settleMode3: settleMode3,
happenedObservations: happenedObservations,
strike2: param.Strike2,
strike3: param.Strike3,
coupon2: param.Coupon2,
multiplier3: param.Multiplier3)
{
UnderlyingTickers = param.underlyingTickers
};
if (string.IsNullOrWhiteSpace(param.tradeId))
{
param.tradeId = Guid.NewGuid().ToString();
}
return new SegmentedAccumulatorOptionTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: accumulatorOption.StartDate,
maturityDate: accumulatorOption.ExerciseDates.Last(),
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: param.notional,
price: 0.0,
option: accumulatorOption)
{
ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve,
MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), accumulatorOption.UnderlyingTickers[0]),
};
}
public static AccumulatorOptionTrade GetAccumulatorOptionTrade(OtcTradeBase td, trade_accumulator_option accumulatorOption
, OptionTradeParamRequest request = null)
{
var param = GetAccumulatorOptionTradeParam(td, accumulatorOption, request);
return GetAccumulatorOptionTrade(param);
}
public static AccumulatorOptionTradeParam GetAccumulatorOptionTradeParam(OtcTradeBase td, trade_accumulator_option accumulatorOption
, OptionTradeParamRequest request)
{
if (accumulatorOption == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的累计期权交易数据");
}
AccumulatorOptionTradeParam result = null;
if (accumulatorOption.AccumulatorStructureType == AccumulatorStructureTypeEnum.Segmented)
{
var segmentedResult = BuildTradeParamBase(td, request);
segmentedResult.Multiplier1 = (accumulatorOption.Multiplier ?? 0);
segmentedResult.Multiplier2 = (accumulatorOption.Multiplier2 ?? 0);
segmentedResult.Multiplier3 = (accumulatorOption.Multiplier3 ?? 0);
segmentedResult.Coupon2 = accumulatorOption.CouponPercent ? (accumulatorOption.Coupon2 ?? 0) * (td.SpotPrice ?? 0) : (accumulatorOption.Coupon2 ?? 0);
segmentedResult.SettleMode2 = accumulatorOption.SettlementMode2;
segmentedResult.SettleMode3 = accumulatorOption.SettlementMode3;
if (segmentedResult.isMoneynessOption)
{
segmentedResult.Strike2 = (accumulatorOption.Strike2 ?? 0) * segmentedResult.initialSpotPrice;
segmentedResult.Strike3 = (accumulatorOption.Strike3 ?? 0) * segmentedResult.initialSpotPrice;
}
else
{
segmentedResult.Strike2 = (accumulatorOption.Strike2 ?? 0);
segmentedResult.Strike3 = (accumulatorOption.Strike3 ?? 0);
}
result = segmentedResult;
result.initialSpotPrice = segmentedResult.initialSpotPrice;
}
else
{
result = BuildTradeParamBase(td, request);
result.PutMultiplier = accumulatorOption.PutMultiplier;
result.CallMultiplier = accumulatorOption.CallMultiplier;
}
result.EarlyTerminate = accumulatorOption.EarlyTerminate;
result.PayCoupon = accumulatorOption.PayoffType == "固定";
result.couponDayCount = accumulatorOption.CouponDayCount.TrimToNull() ?? "Act365"; ;
result.couponPaymentDateStr = accumulatorOption.KOObservationSettleDates;
result.isFixedCoupon = accumulatorOption.IsFixedCoupon;
result.includeStartDateCoupon = false;
result.AccumuType = accumulatorOption.AccumuType;
result.SettlementMode = accumulatorOption.SettlementMode;
//barrier
result.Barrier = accumulatorOption.KOBarrier.HasValue
? (result.isMoneynessOption ? accumulatorOption.KOBarrier.Value * result.initialSpotPrice : accumulatorOption.KOBarrier.Value)
: (result.optionType == OptionType.Call ? 1e10 : -1e10);
if (result.isMoneynessOption)
{
result.strike *= result.initialSpotPrice;
}
result.isMoneynessOption = false;
result.Coupon = accumulatorOption.CouponPercent ? accumulatorOption.Coupon * (td.SpotPrice ?? 0) : accumulatorOption.Coupon;
result.KOObservationDates = accumulatorOption.KOObservationDates;
// Removed updownPriceMode as it's not supported in QDP AccumulatorOption constructor
// result.updownPriceMode = GlobalConfig.AccumulatorCriticalMode == "updown";
result.happenedObservations = request?.happenedObservations;
//将累计期权引擎需要传入的notional从原来的观察日总数量更正为沽购数量
result.notional = accumulatorOption.AccumuTradeAmount * (td.CountRatio ?? 1);
return result;
}
#endregion
#region ----现金流交易----
public static DepositTrade GetCashFlowTrade(CashFlowTradeParam param, double stockEqvNotional)
{
var optionStartDate = param.startDate;
var underlyingMaturityDate = param.endDate;
var deposit = new Deposit(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
depositRate: param.ProfitRate,
dayCount: param.ProfitDayCount.ToDayCountImpl(),
calendar: CalendarImpl.Get("chn"),
bda: BusinessDayConvention.None,
currency: CurrencyCode.CNY,
notional: stockEqvNotional,
annualizedRate: param.RateType == CashFlowRateTypeEnum.年化利率,
depositType: param.DepositType == CashflowDepositTypeEnum.成本摊还 ? DepositType.CostRedemption : DepositType.Regular,
payCouponOnly: true
);
return new DepositTrade(
tradeId: param.tradeId,
tradeDate: param.tradeDate,
startDate: param.startDate,
maturityDate: underlyingMaturityDate,
tradeType: QdpConverter.ConvertTradeType(param.buysell),
notional: stockEqvNotional,
price: 0.0,
instrument: deposit)
{
ValuationParameters = new SimpleCfValuationParameters("RiskFreeDiscountCurve", "RiskFreeDiscountCurve", "RiskFreeDiscountCurve")
};
}
public static DepositTrade GetCashFlowTrade(OtcTradeBase td, trade_cashflow cashflowTrade, OptionTradeParamRequest request = null)
{
var param = GetCashFlowTradeParam(td, cashflowTrade, request);
return GetCashFlowTrade(param, td.StockEqvNotional);
}
public static CashFlowTradeParam GetCashFlowTradeParam(OtcTradeBase td, trade_cashflow cashflow, OptionTradeParamRequest request)
{
if (cashflow == null)
{
throw new TradeCalcExpception(td.id, "未获取到对应的现金流交易数据");
}
var result = BuildTradeParamBase(td, request);
result.ProfitRate = cashflow.ProfitRate;
result.RateType = cashflow.RateType;
result.ProfitDayCount = cashflow.ProfitDayCount;
result.DepositType = cashflow.DepositType;
result.PrepayRatio = cashflow.PrepayRatio;
return result;
}
#endregion
#region ----结构化产品----
public static StructProductTradeParam GetStructProductTradeParam(OptionValueCalcRequest req, MarketProxy marketProxy, trade trade, underlying_manager underlying = null)
{
if (underlying == null)
{
underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
}
var dzStructureService = new DZStructureService(OptUserInfo.SystemUser);
var components = new StructureComponent[4];
var componentsLength = 0;
if (trade.MetaDic.ContainsKey("structures"))
{
var structures = dzStructureService.getStructureList(trade.MetaDic["structures"]);
if (structures != null && structures.Count > 0)
{
structures.ForEach(p =>
{
if (!"None".Equals(p.OptionType))
{
components[componentsLength] = new StructureComponent
{
componentType = p.OptionType,
strike = p.Strike != null ? (double)p.Strike : 0,
barrier = p.BarrierPrice != null ? (double)p.BarrierPrice : 0,
multiplier = p.Multiplier != null ? (double)p.Multiplier : 0,
ramp = string.IsNullOrEmpty(p.RAMP) ? 0 : double.Parse(p.RAMP),
isAccAtStrike = p.IsAccAlSrike == null ? false : (bool)p.IsAccAlSrike
};
componentsLength++;
}
});
}
}
else
{
throw new Exception("未设置结构要素");
}
var scheduleList = new List();
if (trade.MetaDic.ContainsKey("observationDate"))
{
var observationDateStr = trade.MetaDic["observationDate"];
if (!string.IsNullOrEmpty(observationDateStr))
{
var arr = observationDateStr.Split(new char[] { ';' });
var dateArr = arr[0].Split(new char[] { ',' });
var fixedPriceArr = arr[1].Split(new char[] { ',' });
var isClosedArr = arr[2].Split(new char[] { ',' });
var closePriceArr = arr[3].Split(new char[] { ',' });
var arrLength = dateArr.Length;
for (int i = 0; i < arrLength; i++)
{
StructureObsSchedule sos = new StructureObsSchedule
{
valueDate = GetTotalSecends(DateTime.Parse(dateArr[i])),
fixedPrice = string.IsNullOrEmpty(fixedPriceArr[i]) ? 0 : double.Parse(fixedPriceArr[i]),
isClosed = string.IsNullOrEmpty(isClosedArr[i]) ? false : bool.Parse(isClosedArr[i]),
closePrice = string.IsNullOrEmpty(closePriceArr[i]) ? 0 : double.Parse(closePriceArr[i]),
};
scheduleList.Add(sos);
}
}
}
else
{
throw new Exception("未设置观察日");
}
var scheduleArr = scheduleList.ToArray();
var volsurface = new VolSurface()
{
};
var vols = VolatilityHelper.GetVol((DateTime)trade.TradeDate, "交易", underlying.UnderlyingCode, "");
if (vols != null && vols.VolTable != null && vols.VolTable.Any())
{
var strikeArr = vols.VolTable.Select(p => p.Strike).Distinct().OrderBy(p => p).ToArray();
var expireArr = vols.VolTable.Select(p => p.Expire).Distinct().OrderBy(p => p).ToList();
var verticalAxisList = new List();
expireArr.ForEach(p =>
{
if (p.EndsWith("D"))
{
verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)), DayStr = p });
}
else if (p.EndsWith("M"))
{
verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)) * 365 / 12, DayStr = p });
}
else if (p.EndsWith("Y"))
{
verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)) * 365, DayStr = p });
}
else if (p.EndsWith("W"))
{
verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)) * 7, DayStr = p });
}
});
verticalAxisList = verticalAxisList.OrderBy(p => p.Days).ToList();
List flattenedVolList = new List();
foreach (var hor in strikeArr)
{
foreach (var ver in verticalAxisList)
{
flattenedVolList.Add(vols.VolTable.First(p => p.Strike == hor && p.Expire == ver.DayStr).Vol);
}
}
volsurface.horizontalAxis = Marshal.UnsafeAddrOfPinnedArrayElement(strikeArr, 0);
volsurface.horizontalAxisLength = strikeArr.Length;
var verticalAxisArr = verticalAxisList.Select(p => p.Days).ToArray();
volsurface.verticalAxis = Marshal.UnsafeAddrOfPinnedArrayElement(verticalAxisArr, 0);
volsurface.verticalAxisLength = verticalAxisArr.Length;
var flattenedVolArr = flattenedVolList.ToArray();
volsurface.flattenedVol = Marshal.UnsafeAddrOfPinnedArrayElement(flattenedVolArr, 0);
volsurface.flattenedVolLength = flattenedVolArr.Length;
log.Info("期权定价波动率请求1:" + JsonHelper.Serialize(strikeArr));
log.Info("期权定价波动率请求2:" + JsonHelper.Serialize(verticalAxisArr));
log.Info("期权定价波动率请求3:" + JsonHelper.Serialize(flattenedVolArr));
}
int isCashSettle = 1;
if (trade.MetaDic.ContainsKey("cashOrPhysical") && !"Cash".Equals(trade.MetaDic["cashOrPhysical"]))
{
isCashSettle = 0;
}
TradeValueResult result = new TradeValueResult();
var request = new StructureRequest
{
buySell = "卖出".Equals(trade.BuySell) ? -1 : 1,
r = req.sysRiskFreeRate,
constantVol = req.vols[0],
underlierPrice = req.spotPrices[0],
valueTime = GetTotalSecends(marketProxy.ValueDate),
isCashSettle = isCashSettle,
scenarioPrice = 0, //情景价格,计算预付金使用 标的价格*涨跌幅 = 情景价格,调用dll
component = components,
componentLength = componentsLength,
//volSurface = new IntPtr(&volsurface),
schedule = Marshal.UnsafeAddrOfPinnedArrayElement(scheduleArr, 0),
scheduleLength = scheduleArr.Length,
valueType = "a"
};
log.Info("期权定价观察日请求:" + JsonHelper.Serialize(scheduleArr));
StructureResult sResult = new StructureResult
{
message = new string(' ', 100),
pv = 0,
delta = 0,
gamma = 0,
thetaPerDay = 0,
rhoPercentage = 0,
vegaPercentage = 0,
};
return new StructProductTradeParam { Request = request, VolSurface = volsurface };
}
private static long GetTotalSecends(DateTime date)
{
return (long)(date - DateTime.Parse("1970-1-1")).TotalSeconds;
}
#endregion
#region----内部方法----
private static void CheckOptionTradeParam(OptionTradeParamBase param)
{
if (param is null)
{
throw new ArgumentNullException(nameof(param));
}
if (param.underlyingTickers == null || !param.underlyingTickers.Any())
{
throw new Exception("缺少标的代码");
}
}
///
/// QDP计算参数基础设置
///
private static T BuildTradeParamBase(OtcTradeBase td, OptionTradeParamRequest request) where T : OptionTradeParamBase, new()
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
request ??= new OptionTradeParamRequest(valuedateBLL.RiskFreeRate / 100);
//----------------------------------
// 补足交易数据缺失
//----------------------------------
if (!td.StartDate.HasValue)
{
td.StartDate = td.TradeDate;
}
if (td.ExerciseDate == null)
{
throw new ServiceException("缺少到期日");
}
if (td.HasUnderlying())
{
if (string.IsNullOrWhiteSpace(td.UnderlyingCode))
{
throw new ServiceException("交易参数缺少标的代码");
}
if (string.IsNullOrWhiteSpace(td.UnderlyingInstrumentType))
{
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
if (un == null)
{
throw new ServiceException("交易参数找不到标的数据:" + td.UnderlyingCode);
}
td.UnderlyingInstrumentType = un.UnderlyingInstrumentType;
}
}
//----------------------------------
// 生成计算参数对象
//----------------------------------
var param = new T
{
underlyingTickers = new[] { td.UnderlyingCode },
underlyingInstrumentType = td.UnderlyingInstrumentType,
hasNightMarket = request.hasNightMarket,
startDate = new Date(td.StartDate.Value),
originalExerciseDate = td.ExerciseDate.Value,
exerciseDate = GetExerciseDate(td.ExerciseDate, request.maturityShift),
endDate = GetEndDate(td.MaturityDate ?? td.ExerciseDate, request.maturityShift),
settlementDate = td.SettlementDate,
buysell = td.BuySell,
optionType = QdpConverter.ConvertOptionType(td.CallPut),
exerciseType = td.ExerciseMode,
strike = td.Strike ?? 0,
initialSpotPrice = td.SpotPrice ?? 0,
isMoneynessOption = td.IsMoneynessOptionData,
isMoneynessOptionOrigianl = td.IsMoneynessOptionData,
participationRate = td.ParticipationRate ?? 1.0,
principalRate = td.PrincipalRate ?? 0,
isAnnualized = td.IsAnnualized,
annualizedFactor = td.AnnualizeFactor ?? 1.0,
notional = td.Notional,
dividendRate = td.DividendRate ?? td.NoRiskRate ?? request.sysRiskFreeRate,
riskFreeRate = td.NoRiskRate ?? request.sysRiskFreeRate,
preciseTimeMode = request.preciseTimeMode,
tradeId = request.tradeId,
tradeNumber = td.TradeNumber,
timeToMaturityDays = request.timeToMaturityDays,
dividends = request.dividends,
volSurfaceNames = request.volSurfaceNames
};
//if (td.TradeType != param.tradeType && (td.TradeType != "场内期权" || param.tradeType != "香草期权"))
//{
// throw new ArgumentNullException($"结构类型不符,期望的结构类型为'{param.tradeType}',但传入结构类型为'{td.TradeType}'");
//}
request.ParamOverride?.Invoke(param);
return param;
}
///
/// 获取结束日(endDate必须有值否则QDP会报错)
///
private static Date GetEndDate(DateTime? maturityDate, int maturityShift)
{
if (maturityDate == null || DateTime.Now.AddYears(3) < maturityDate.Value)
{
maturityDate = DateTime.Today.AddYears(3);
}
var endDate = QdpCalendarHelper.BizDayShift2(maturityDate.Value);
if (maturityShift != 0)
{
var dayCount = CalculatorHelper.GetTradeDayCount();
endDate = QdpCalendarHelper.ShiftDate(endDate, dayCount, maturityShift);
}
return endDate;
}
///
/// 获取行权日
///
private static Date GetExerciseDate(DateTime? exerciseDate, int maturityShift)
{
if (exerciseDate != null)
{
var date = exerciseDate.Value;
if (maturityShift != 0)
{
var dayCount = CalculatorHelper.GetTradeDayCount();
date = QdpCalendarHelper.ShiftDate(date, dayCount, maturityShift);
}
return date;
}
return null;
}
#endregion
}
///
/// 雪球期权类型
///
public enum SnowBallOptionMode
{
///
/// 根据传入参数自动判别
///
Auto,
///
/// 普通雪球
///
SimpleSnowball,
///
/// 保本雪球
///
AnnualizedPremiumSnowball
}
}