using Microsoft.VisualStudio.TestTools.UnitTesting; using Qdp.Pricing.Base.Enums; using System; using YLErp.BLL; using YLErp.BLL.Calculation; using YLErp.DBModels; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { /// /// 期权计算比较(新版本和老版本) /// [TestClass] public class OptionCalculatorCompare { const double ConstVol = 0.3; const double Notional = 1; const double SpotPrice = 3000; const double RiskFreeRate = 0.03; const string ExerciseType = "European"; const string UnderlyingCode = "RB00"; const int TTMDays = 36; const string InstrumentType = "CommodityFutures"; static readonly string QdpMarketID = Guid.NewGuid().ToString(); static readonly DateTime TradeDate = new DateTime(2021, 1, 4); static readonly DateTime ExerciseDate = new DateTime(2021, 3, 1); [TestMethod] public void TestVanillaOption() { var vols = QdpVolHelper.GetDefaultVolatility(ConstVol); var valueDateStr = TradeDate.ToString("yyyy-MM-dd"); var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(QdpMarketID); var underlying = new { UnderlyingCode = UnderlyingCode, UnderlyingInstrumentType = InstrumentType, Price = SpotPrice }; //使用全局的DiscountCurve以提高计算效率 var discountCurveName = Guid.NewGuid().ToString(); var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, valuedateBLL.RiskFreeRate / 100.0); marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve); var initParam = new VolSurfaceInitParamsBuilder(QdpMarketID) .SetValueDate(TradeDate) .SetUnderlying(0, UnderlyingCode, UnderlyingCode) .SetVolatility(vols).Build(QdpMarketID); VolSurfaceInitializerSingleton.GetInitializer(false).InitializeMarketProxy(initParam); var bidMaturityDate = ExerciseDate.ToString(ConsGlobal.DateFormat); var tv1 = OptionCalculatorV1.ValueVanillaOptionTrade( marketProxy: marketProxy, valueDate: valueDateStr, underlyingTicker: underlying.UnderlyingCode, underlyingInstrumentType: underlying.UnderlyingInstrumentType, strike: SpotPrice, startDate: valueDateStr, endDate: bidMaturityDate, optionType: "Call", exerciseType: ExerciseType, spotPrice: underlying.Price, notional: Notional, volSurfaceName: initParam.volSurfaceNameKey, riskFreeRate: RiskFreeRate, modelName: null, tradeType: "Buy", exerciseDate: bidMaturityDate, hasNightMarket: false, commodityFuturesPreciseTimeMode: true, discountCurveName: discountCurveName, participationRate: 1.0, principalRate: 0.0, isAnnualized: false, annualizeFactor: 1.0, timeToMaturityDays: TTMDays); System.Diagnostics.Debug.WriteLine("V1 PV:" + tv1.Pv); var vtParam = new VanillaOptionTradeParam { annualizedFactor = 0, isAnnualized = false, buysell = "买入", commodityFuturesPreciseTimeMode = true, dividendRate = 0, dividends = null, endDate = ExerciseDate, exerciseDate = ExerciseDate, exerciseType = ExerciseType, hasNightMarket = false, initialSpotPrice = SpotPrice, isForwardTrade = false, isMoneynessOption = false, notional = Notional, optionType = OptionType.Call, participationRate = 1, principalRate = 0, riskFreeRate = RiskFreeRate, settlementDate = ExerciseDate, startDate = TradeDate, strike = SpotPrice, timeToMaturityDays = TTMDays, tradeDate = TradeDate, tradeId = QdpMarketID, underlyingInstrumentType = InstrumentType, underlyingTickers = new[] { UnderlyingCode }, volSurfaceNames = new[] { QdpMarketID } }; TradeValueResult tv2; using (var mp = new MarketProxy(TradeDate, 0.03)) { mp.SaveVolSurface(QdpMarketID, vols); tv2 = TradeRiskCalcUtil.GetVanillaOptionValue(mp, new OptionCalcParam(vtParam) { pricingRequest = QdpPricingRequest.BASIC_GREEKS, spotPrices = new[] { 3000d }, }); } System.Diagnostics.Debug.WriteLine("V2 PV:" + tv2.Pv); Assert.AreEqual(tv1.Pv, tv2.Pv, 1e-6); Assert.AreEqual(tv1.Delta, tv2.Delta, 1e-6); Assert.AreEqual(tv1.Gamma, tv2.Gamma, 1e-6); Assert.AreEqual(tv1.Theta, tv2.Theta, 1e-6); Assert.AreEqual(tv1.Rho, tv2.Rho, 1e-6); Assert.AreEqual(tv1.Vega, tv2.Vega, 1e-6); } [TestMethod] public void TestAsiaOption() { var vols = QdpVolHelper.GetDefaultVolatility(ConstVol); var valueDateStr = TradeDate.ToString("yyyy-MM-dd"); var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(QdpMarketID); var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(UnderlyingCode); //使用全局的DiscountCurve以提高计算效率 var discountCurveName = Guid.NewGuid().ToString(); var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, valuedateBLL.RiskFreeRate / 100.0); marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve); var initParam = new VolSurfaceInitParamsBuilder(QdpMarketID) .SetValueDate(TradeDate) .SetUnderlying(0, UnderlyingCode, UnderlyingCode) .SetVolatility(vols).Build(QdpMarketID); VolSurfaceInitializerSingleton.GetInitializer(false).InitializeMarketProxy(initParam); underlying.Price = SpotPrice; underlying.QuotationDate = TradeDate; var td = new trade() { TradeType = "亚式期权", UnderlyingCode = underlying.UnderlyingCode, UnderlyingInstrumentType = InstrumentType, TradeDate = TradeDate, StartDate = TradeDate, MaturityDate = ExerciseDate, ExerciseDate = ExerciseDate, OptionType = "看涨", ExerciseMode = ExerciseType, Strike = SpotPrice, SpotPrice = SpotPrice, Notional = Notional, NoRiskRate = RiskFreeRate, BuySell = "Buy", QuotationType = "波动率调整", TradeOpenVolatility = ConstVol, TTMDays = TTMDays, trade_asian_option = new trade_asian_option() { PayoffType = "ArithmeticAverage", StrikeType = "Fixed", AveragingPeriodStartDate = TradeDate } }; var tv1 = OptionCalculatorV1.GetOptionValueResult(QdpMarketID, underlying, td, new[] { SpotPrice }, useTradeVolMode: true, volSurfaceNames: new[] { initParam.volSurfaceNameKey }, fixing: $"{TradeDate:yyyy-MM-dd},{SpotPrice}", commodityFuturesPreciseTimeMode: false); System.Diagnostics.Debug.WriteLine("V1 PV:" + tv1.Pv); var vtParam = new AsianOptionTradeParam { annualizedFactor = 0, isAnnualized = false, buysell = "买入", commodityFuturesPreciseTimeMode = false, dividendRate = 0, dividends = null, endDate = ExerciseDate, exerciseDate = ExerciseDate, exerciseType = ExerciseType, hasNightMarket = false, initialSpotPrice = SpotPrice, isForwardTrade = false, isMoneynessOption = false, notional = 1, optionType = OptionType.Call, participationRate = 1, principalRate = 0, riskFreeRate = RiskFreeRate, settlementDate = ExerciseDate, startDate = TradeDate, strike = SpotPrice, timeToMaturityDays = TTMDays, tradeDate = TradeDate, tradeId = QdpMarketID, underlyingInstrumentType = InstrumentType, underlyingTickers = new[] { UnderlyingCode }, volSurfaceNames = new[] { QdpMarketID }, payoffType = "ArithmeticAverage", strikeStyle = "Fixed", averagingPeriodStartDate = TradeDate }; TradeValueResult tv2; using (var mp = new MarketProxy(TradeDate, RiskFreeRate)) { mp.SaveVolSurface(QdpMarketID, vols); tv2 = TradeRiskCalcUtil.GetAsianOptionValue(mp, new OptionCalcParam(vtParam) { pricingRequest = QdpPricingRequest.BASIC_GREEKS, spotPrices = new[] { SpotPrice }, }); } System.Diagnostics.Debug.WriteLine("V2 PV:" + tv2.Pv); Assert.AreEqual(tv1.Pv, tv2.Pv, 1e-6); Assert.AreEqual(tv1.Delta, tv2.Delta, 1e-6); Assert.AreEqual(tv1.Gamma, tv2.Gamma, 1e-6); Assert.AreEqual(tv1.Theta, tv2.Theta, 1e-6); Assert.AreEqual(tv1.Rho, tv2.Rho, 1e-6); Assert.AreEqual(tv1.Vega, tv2.Vega, 1e-6); } } }