//------------------------------------------------------------------------- public virtual void test_price_presentValue() { for (int i = 0; i < NB_STRIKES; ++i) { ResolvedFxVanillaOption call = CALLS[i]; ResolvedFxVanillaOptionTrade callTrade = ResolvedFxVanillaOptionTrade.builder().product(call).premium(Payment.of(EUR, 0, VAL_DATE)).build(); double computedPriceCall = PRICER.price(call, RATES_PROVIDER, VOLS); CurrencyAmount computedCall = PRICER.presentValue(call, RATES_PROVIDER, VOLS); double timeToExpiry = VOLS.relativeTime(EXPIRY); FxRate forward = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER); double forwardRate = forward.fxRate(CURRENCY_PAIR); double strikeRate = call.Strike; SmileDeltaParameters smileAtTime = VOLS.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double df = RATES_PROVIDER.discountFactor(USD, PAY); double[] weights = this.weights(forwardRate, strikeRate, strikes, timeToExpiry, vols[1]); double expectedPriceCall = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, vols[1], true); for (int j = 0; j < 3; ++j) { expectedPriceCall += weights[j] * (BlackFormulaRepository.price(forwardRate, strikes[j], timeToExpiry, vols[j], true) - BlackFormulaRepository.price(forwardRate, strikes[j], timeToExpiry, vols[1], true)); } expectedPriceCall *= df; assertEquals(computedPriceCall, expectedPriceCall, TOL); assertEquals(computedCall.Amount, expectedPriceCall * NOTIONAL, TOL * NOTIONAL); // test against trade pricer assertEquals(computedCall, TRADE_PRICER.presentValue(callTrade, RATES_PROVIDER, VOLS).getAmount(USD)); } }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity of the swaption product to the rate curves. /// <para> /// The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the /// curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential /// re-calibration of the model parameters to the raw market data. /// /// </para> /// </summary> /// <param name="swaption"> the swaption product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the point sensitivity to the rate curves </returns> public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyModel(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); ZonedDateTime expiryDateTime = swaption.Expiry; double expiry = swaptionVolatilities.relativeTime(expiryDateTime); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); ValueDerivatives annuityDerivative = SwapPricer.LegPricer.annuityCashDerivative(fixedLeg, forward); double annuityCash = annuityDerivative.Value; double annuityCashDr = annuityDerivative.getDerivative(0); LocalDate settlementDate = ((CashSwaptionSettlement)swaption.SwaptionSettlement).SettlementDate; double discountSettle = ratesProvider.discountFactor(fixedLeg.Currency, settlementDate); double strike = calculateStrike(fixedLeg); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double shift = swaptionVolatilities.shift(expiry, tenor); ValueDerivatives volatilityAdj = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward); bool isCall = fixedLeg.PayReceive.Pay; double shiftedForward = forward + shift; double shiftedStrike = strike + shift; double price = BlackFormulaRepository.price(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall); double delta = BlackFormulaRepository.delta(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall); double vega = BlackFormulaRepository.vega(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value); PointSensitivityBuilder forwardSensi = SwapPricer.parRateSensitivity(underlying, ratesProvider); PointSensitivityBuilder discountSettleSensi = ratesProvider.discountFactors(fixedLeg.Currency).zeroRatePointSensitivity(settlementDate); double sign = swaption.LongShort.sign(); return(forwardSensi.multipliedBy(sign * discountSettle * (annuityCash * (delta + vega * volatilityAdj.getDerivative(0)) + annuityCashDr * price)).combinedWith(discountSettleSensi.multipliedBy(sign * annuityCash * price))); }
//------------------------------------------------------------------------- /// <summary> /// Calculates the present value sensitivity to the SABR model parameters of the swaption product. /// <para> /// The sensitivity of the present value to the SABR model parameters, alpha, beta, rho and nu. /// /// </para> /// </summary> /// <param name="swaption"> the swaption product </param> /// <param name="ratesProvider"> the rates provider </param> /// <param name="swaptionVolatilities"> the volatilities </param> /// <returns> the point sensitivity to the SABR model parameters </returns> public virtual PointSensitivityBuilder presentValueSensitivityModelParamsSabr(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities) { validate(swaption, ratesProvider, swaptionVolatilities); double expiry = swaptionVolatilities.relativeTime(swaption.Expiry); ResolvedSwap underlying = swaption.Underlying; ResolvedSwapLeg fixedLeg = this.fixedLeg(underlying); double tenor = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate); double shift = swaptionVolatilities.shift(expiry, tenor); double strike = calculateStrike(fixedLeg); if (expiry < 0d) { // Option has expired already return(PointSensitivityBuilder.none()); } double forward = SwapPricer.parRate(underlying, ratesProvider); double volatility = swaptionVolatilities.volatility(expiry, tenor, strike, forward); double numeraire = calculateNumeraire(swaption, fixedLeg, forward, ratesProvider); DoubleArray derivative = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward).Derivatives; double vega = numeraire * swaption.LongShort.sign() * BlackFormulaRepository.vega(forward + shift, strike + shift, expiry, volatility); // sensitivities Currency ccy = fixedLeg.Currency; SwaptionVolatilitiesName name = swaptionVolatilities.Name; return(PointSensitivityBuilder.of(SwaptionSabrSensitivity.of(name, expiry, tenor, ALPHA, ccy, vega * derivative.get(2)), SwaptionSabrSensitivity.of(name, expiry, tenor, BETA, ccy, vega * derivative.get(3)), SwaptionSabrSensitivity.of(name, expiry, tenor, RHO, ccy, vega * derivative.get(4)), SwaptionSabrSensitivity.of(name, expiry, tenor, NU, ccy, vega * derivative.get(5)))); }
//------------------------------------------------------------------------- public virtual void test_presentValueSensitivity() { for (int i = 0; i < NB_STRIKES; ++i) { ResolvedFxVanillaOption option = CALLS[i]; PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(option, RATES_PROVIDER, VOLS); CurrencyParameterSensitivities sensiComputed = RATES_PROVIDER.parameterSensitivity(point.build()); double timeToExpiry = VOLS.relativeTime(EXPIRY); double forwardRate = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER).fxRate(CURRENCY_PAIR); double strikeRate = option.Strike; SmileDeltaParameters smileAtTime = VOLS.Smile.smileForExpiry(timeToExpiry); double[] vols = smileAtTime.Volatility.toArray(); double df = RATES_PROVIDER.discountFactor(USD, PAY); CurrencyParameterSensitivities sensiExpected = FD_CAL.sensitivity(RATES_PROVIDER, p => PRICER.presentValue(option, p, VOLS)); CurrencyParameterSensitivities sensiRes = FD_CAL.sensitivity(RATES_PROVIDER, (ImmutableRatesProvider p) => { double fwd = FX_PRICER.forwardFxRate(option.Underlying, p).fxRate(CURRENCY_PAIR); double[] strs = smileAtTime.strike(fwd).toArray(); double[] wghts = weights(fwd, strikeRate, strs, timeToExpiry, vols[1]); double res = 0d; for (int j = 0; j < 3; ++j) { res += wghts[j] * (BlackFormulaRepository.price(forwardRate, strs[j], timeToExpiry, vols[j], true) - BlackFormulaRepository.price(forwardRate, strs[j], timeToExpiry, vols[1], true)); } return(CurrencyAmount.of(USD, -res * df * NOTIONAL)); }); assertTrue(sensiComputed.equalWithTolerance(sensiExpected.combinedWith(sensiRes), FD_EPS * NOTIONAL * 10d)); } }
/// <summary> /// Calculates the price sensitivity to the Black volatility used for the pricing of the bond future option /// based on the price of the underlying future. /// </summary> /// <param name="futureOption"> the option product </param> /// <param name="discountingProvider"> the discounting provider </param> /// <param name="volatilities"> the volatilities </param> /// <param name="futurePrice"> the underlying future price </param> /// <returns> the sensitivity </returns> public BondFutureOptionSensitivity priceSensitivityModelParamsVolatility(ResolvedBondFutureOption futureOption, LegalEntityDiscountingProvider discountingProvider, BlackBondFutureVolatilities volatilities, double futurePrice) { ArgChecker.isTrue(futureOption.PremiumStyle.Equals(FutureOptionPremiumStyle.DAILY_MARGIN), "Premium style should be DAILY_MARGIN"); double strike = futureOption.StrikePrice; ResolvedBondFuture future = futureOption.UnderlyingFuture; double volatility = volatilities.volatility(futureOption.Expiry, future.LastTradeDate, strike, futurePrice); double timeToExpiry = volatilities.relativeTime(futureOption.Expiry); double vega = BlackFormulaRepository.vega(futurePrice, strike, timeToExpiry, volatility); return(BondFutureOptionSensitivity.of(volatilities.Name, timeToExpiry, future.LastTradeDate, strike, futurePrice, future.Currency, vega)); }
/// <summary> /// Calculates the theta of the bond future option product based on the price of the underlying future. /// <para> /// The theta of the product is minus of the option price sensitivity to the time to expiry. /// The volatility is unchanged for a fixed strike in the sensitivity computation, hence the "StickyStrike" name. /// /// </para> /// </summary> /// <param name="futureOption"> the option product </param> /// <param name="discountingProvider"> the discounting provider </param> /// <param name="volatilities"> the volatilities </param> /// <param name="futurePrice"> the price of the underlying future </param> /// <returns> the price curve sensitivity of the product </returns> public double theta(ResolvedBondFutureOption futureOption, LegalEntityDiscountingProvider discountingProvider, BlackBondFutureVolatilities volatilities, double futurePrice) { ArgChecker.isTrue(futureOption.PremiumStyle.Equals(FutureOptionPremiumStyle.DAILY_MARGIN), "Premium style should be DAILY_MARGIN"); double strike = futureOption.StrikePrice; ResolvedBondFuture future = futureOption.UnderlyingFuture; double volatility = volatilities.volatility(futureOption.Expiry, future.LastTradeDate, strike, futurePrice); double timeToExpiry = volatilities.relativeTime(futureOption.Expiry); double theta = BlackFormulaRepository.driftlessTheta(futurePrice, strike, timeToExpiry, volatility); return(theta); }
//------------------------------------------------------------------------- public virtual void test_theta_presentValueTheta() { double theta = PRICER.theta(CALL_OTM, RATES_PROVIDER, VOLS); CurrencyAmount pvTheta = PRICER.presentValueTheta(CALL_OTM, RATES_PROVIDER, VOLS); double timeToExpiry = VOLS.relativeTime(EXPIRY); double dfDom = RATES_PROVIDER.discountFactor(USD, PAYMENT_DATE); double forward = PRICER.DiscountingFxSingleProductPricer.forwardFxRate(FX_PRODUCT_HIGH, RATES_PROVIDER).fxRate(CURRENCY_PAIR); double vol = SMILE_TERM.volatility(timeToExpiry, STRIKE_RATE_HIGH, forward); double expectedTheta = dfDom * BlackFormulaRepository.driftlessTheta(forward, STRIKE_RATE_HIGH, timeToExpiry, vol); assertEquals(theta, expectedTheta, TOL); double expectedPvTheta = -NOTIONAL *dfDom *BlackFormulaRepository.driftlessTheta(forward, STRIKE_RATE_HIGH, timeToExpiry, vol); assertEquals(pvTheta.Currency, USD); assertEquals(pvTheta.Amount, expectedPvTheta, NOTIONAL * TOL); }
/// <summary> /// Tests the strikes computations. /// </summary> public virtual void strike() { double[] strike = SMILE.strike(FORWARD).toArrayUnsafe(); DoubleArray volatility = SMILE.Volatility; int nbDelta = DELTA.size(); for (int loopdelta = 0; loopdelta < nbDelta; loopdelta++) { ValueDerivatives dPut = BlackFormulaRepository.priceAdjoint(FORWARD, strike[loopdelta], TIME_TO_EXPIRY, volatility.get(loopdelta), false); assertEquals(-DELTA.get(loopdelta), dPut.getDerivative(0), 1e-8, "Strike: Put " + loopdelta); ValueDerivatives dCall = BlackFormulaRepository.priceAdjoint(FORWARD, strike[2 * nbDelta - loopdelta], TIME_TO_EXPIRY, volatility.get(2 * nbDelta - loopdelta), true); assertEquals(DELTA.get(loopdelta), dCall.getDerivative(0), 1e-8, "Strike: Call " + loopdelta); } ValueDerivatives dPut = BlackFormulaRepository.priceAdjoint(FORWARD, strike[nbDelta], TIME_TO_EXPIRY, volatility.get(nbDelta), false); ValueDerivatives dCall = BlackFormulaRepository.priceAdjoint(FORWARD, strike[nbDelta], TIME_TO_EXPIRY, volatility.get(nbDelta), true); assertEquals(0.0, dCall.getDerivative(0) + dPut.getDerivative(0), 1e-8, "Strike: ATM"); }
//------------------------------------------------------------------------- private double[] weights(double forward, double strike, double[] strikes, double timeToExpiry, double atmVol) { //JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java: //ORIGINAL LINE: double[][] mat = new double[3][3]; double[][] mat = RectangularArrays.ReturnRectangularDoubleArray(3, 3); double[] vec = new double[3]; for (int i = 0; i < 3; ++i) { mat[0][i] = BlackFormulaRepository.vega(forward, strikes[i], timeToExpiry, atmVol); mat[1][i] = BlackFormulaRepository.vanna(forward, strikes[i], timeToExpiry, atmVol); mat[2][i] = BlackFormulaRepository.volga(forward, strikes[i], timeToExpiry, atmVol); } vec[0] = BlackFormulaRepository.vega(forward, strike, timeToExpiry, atmVol); vec[1] = BlackFormulaRepository.vanna(forward, strike, timeToExpiry, atmVol); vec[2] = BlackFormulaRepository.volga(forward, strike, timeToExpiry, atmVol); DecompositionResult res = SVD.apply(DoubleMatrix.ofUnsafe(mat)); return(res.solve(vec)); }
//------------------------------------------------------------------------- public virtual void test_gamma_presentValueGamma() { double gammaCall = PRICER.gamma(CALL_OTM, RATES_PROVIDER, VOLS); CurrencyAmount pvGammaCall = PRICER.presentValueGamma(CALL_OTM, RATES_PROVIDER, VOLS); double gammaPut = PRICER.gamma(PUT_ITM, RATES_PROVIDER, VOLS); CurrencyAmount pvGammaPut = PRICER.presentValueGamma(PUT_ITM, RATES_PROVIDER, VOLS); double timeToExpiry = VOLS.relativeTime(EXPIRY); double dfDom = RATES_PROVIDER.discountFactor(USD, PAYMENT_DATE); double dfFor = RATES_PROVIDER.discountFactor(EUR, PAYMENT_DATE); double forward = PRICER.DiscountingFxSingleProductPricer.forwardFxRate(FX_PRODUCT_HIGH, RATES_PROVIDER).fxRate(CURRENCY_PAIR); double vol = SMILE_TERM.volatility(timeToExpiry, STRIKE_RATE_HIGH, forward); double expectedGamma = dfFor * dfFor / dfDom * BlackFormulaRepository.gamma(forward, STRIKE_RATE_HIGH, timeToExpiry, vol); double expectedPvGamma = -NOTIONAL * dfFor * dfFor / dfDom * BlackFormulaRepository.gamma(forward, STRIKE_RATE_HIGH, timeToExpiry, vol); assertEquals(gammaCall, expectedGamma, TOL); assertEquals(pvGammaCall.Currency, USD); assertEquals(pvGammaCall.Amount, expectedPvGamma, NOTIONAL * TOL); assertEquals(gammaPut, expectedGamma, TOL); assertEquals(pvGammaPut.Currency, USD); assertEquals(pvGammaPut.Amount, -expectedPvGamma, NOTIONAL * TOL); }
public virtual void test_recoverVolatility() { int nSteps = TREE_DATA.NumberOfSteps; double spot = TREE_DATA.Spot; double timeToExpiry = TREE_DATA.getTime(nSteps); double dfDom = RATE_PROVIDER.discountFactors(USD).discountFactor(timeToExpiry); double dfFor = RATE_PROVIDER.discountFactors(EUR).discountFactor(timeToExpiry); double forward = spot * dfFor / dfDom; for (int i = 0; i < 100; ++i) { double strike = spot * (0.8 + 0.004 * i); OptionFunction func = EuropeanVanillaOptionFunction.of(strike, timeToExpiry, PutCall.CALL, nSteps); double price = TREE.optionPrice(func, TREE_DATA); double impliedVol = BlackFormulaRepository.impliedVolatility(price / dfDom, forward, strike, timeToExpiry, true); double orgVol = VOLS.volatility(FX_PRODUCT.CurrencyPair, timeToExpiry, strike, forward); assertEquals(impliedVol, orgVol, orgVol * 0.1); // large tol double priceMrkt = TREE.optionPrice(func, TREE_DATA_MRKT); double impliedVolMrkt = BlackFormulaRepository.impliedVolatility(priceMrkt / dfDom, forward, strike, timeToExpiry, true); double orgVolMrkt = VOLS_MRKT.volatility(FX_PRODUCT.CurrencyPair, timeToExpiry, strike, forward); assertEquals(impliedVolMrkt, orgVolMrkt, orgVolMrkt * 0.1); // large tol } }
//------------------------------------------------------------------------- public virtual void test_presentValueSensitivityVolatility() { for (int i = 0; i < NB_STRIKES; ++i) { PointSensitivities computedCall = PRICER.presentValueSensitivityModelParamsVolatility(CALLS[i], RATES_PROVIDER, VOLS).build(); double timeToExpiry = VOLS.relativeTime(EXPIRY); FxRate forward = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER); double forwardRate = forward.fxRate(CURRENCY_PAIR); double strikeRate = CALLS[i].Strike; SmileDeltaParameters smileAtTime = VOLS.Smile.smileForExpiry(timeToExpiry); double[] strikes = smileAtTime.strike(forwardRate).toArray(); double[] vols = smileAtTime.Volatility.toArray(); double df = RATES_PROVIDER.discountFactor(USD, PAY); double[] weights = this.weights(forwardRate, strikeRate, strikes, timeToExpiry, vols[1]); double[] vegas = new double[3]; vegas[2] = BlackFormulaRepository.vega(forwardRate, strikeRate, timeToExpiry, vols[1]) * df * NOTIONAL; for (int j = 0; j < 3; j += 2) { vegas[2] -= weights[j] * NOTIONAL *df *BlackFormulaRepository.vega(forwardRate, strikes[j], timeToExpiry, vols[1]); } vegas[0] = weights[0] * NOTIONAL *df *BlackFormulaRepository.vega(forwardRate, strikes[0], timeToExpiry, vols[0]); vegas[1] = weights[2] * NOTIONAL *df *BlackFormulaRepository.vega(forwardRate, strikes[2], timeToExpiry, vols[2]); double[] expStrikes = new double[] { strikes[0], strikes[2], strikes[1] }; for (int j = 0; j < 3; ++j) { FxOptionSensitivity sensi = (FxOptionSensitivity)computedCall.Sensitivities.get(j); assertEquals(sensi.Sensitivity, vegas[j], TOL * NOTIONAL); assertEquals(sensi.Strike, expStrikes[j], TOL); assertEquals(sensi.Forward, forwardRate, TOL); assertEquals(sensi.Currency, USD); assertEquals(sensi.CurrencyPair, CURRENCY_PAIR); assertEquals(sensi.Expiry, timeToExpiry); } } }
public virtual void flatVolPriceTest() { double tol = 2.0e-2; double constantVol = 0.15; double spot = 100d; double maxTime = 1d; int nSteps = 9; ConstantSurface impliedVolSurface = ConstantSurface.of("impliedVol", constantVol); System.Func <double, double> zeroRate = (double?x) => { return(0d); }; System.Func <DoublesPair, ValueDerivatives> func = (DoublesPair x) => { double price = BlackFormulaRepository.price(spot, x.Second, x.First, constantVol, true); return(ValueDerivatives.of(price, DoubleArray.EMPTY)); }; DeformedSurface priceSurface = DeformedSurface.of(DefaultSurfaceMetadata.of("price"), impliedVolSurface, func); ImpliedTrinomialTreeLocalVolatilityCalculator calc = new ImpliedTrinomialTreeLocalVolatilityCalculator(nSteps, maxTime, INTERP_TIMESQ_LINEAR); InterpolatedNodalSurface localVolSurface = calc.localVolatilityFromPrice(priceSurface, spot, zeroRate, zeroRate); assertEquals(localVolSurface.ZValues.Where(d => !DoubleMath.fuzzyEquals(d, constantVol, tol)).Count(), 0); }
//------------------------------------------------------------------------- public virtual void test_price_presentValue() { double priceCallOtm = PRICER.price(CALL_OTM, RATES_PROVIDER, VOLS); CurrencyAmount pvCallOtm = PRICER.presentValue(CALL_OTM, RATES_PROVIDER, VOLS); double pricePutOtm = PRICER.price(PUT_OTM, RATES_PROVIDER, VOLS); CurrencyAmount pvPutOtm = PRICER.presentValue(PUT_OTM, RATES_PROVIDER, VOLS); double timeToExpiry = VOLS.relativeTime(EXPIRY); double df = RATES_PROVIDER.discountFactor(USD, PAYMENT_DATE); double forward = PRICER.DiscountingFxSingleProductPricer.forwardFxRate(FX_PRODUCT_HIGH, RATES_PROVIDER).fxRate(CURRENCY_PAIR); double volHigh = SMILE_TERM.volatility(timeToExpiry, STRIKE_RATE_HIGH, forward); double volLow = SMILE_TERM.volatility(timeToExpiry, STRIKE_RATE_LOW, forward); double expectedPriceCallOtm = df * BlackFormulaRepository.price(forward, STRIKE_RATE_HIGH, timeToExpiry, volHigh, true); double expectedPricePutOtm = df * BlackFormulaRepository.price(forward, STRIKE_RATE_LOW, timeToExpiry, volLow, false); double expectedPvCallOtm = -NOTIONAL *df *BlackFormulaRepository.price(forward, STRIKE_RATE_HIGH, timeToExpiry, volHigh, true); double expectedPvPutOtm = -NOTIONAL *df *BlackFormulaRepository.price(forward, STRIKE_RATE_LOW, timeToExpiry, volLow, false); assertEquals(priceCallOtm, expectedPriceCallOtm, TOL); assertEquals(pvCallOtm.Currency, USD); assertEquals(pvCallOtm.Amount, expectedPvCallOtm, NOTIONAL * TOL); assertEquals(pricePutOtm, expectedPricePutOtm, TOL); assertEquals(pvPutOtm.Currency, USD); assertEquals(pvPutOtm.Amount, expectedPvPutOtm, NOTIONAL * TOL); }
public virtual void test_presentValueTheta_formula_shift() { CurrencyAmount computedCaplet = PRICER.presentValueTheta(CAPLET_LONG, RATES, SHIFTED_VOLS); CurrencyAmount computedFloorlet = PRICER.presentValueTheta(FLOORLET_SHORT, RATES, SHIFTED_VOLS); double forward = RATES.iborIndexRates(EUR_EURIBOR_3M).rate(RATE_COMP.Observation); double expiry = SHIFTED_VOLS.relativeTime(CAPLET_LONG.FixingDateTime); double volatility = SHIFTED_VOLS.volatility(expiry, STRIKE, forward); double df = RATES.discountFactor(EUR, CAPLET_LONG.PaymentDate); double shift = IborCapletFloorletDataSet.SHIFT; double expectedCaplet = NOTIONAL * df * CAPLET_LONG.YearFraction * BlackFormulaRepository.driftlessTheta(forward + shift, STRIKE + shift, expiry, volatility); double expectedFloorlet = -NOTIONAL *df *FLOORLET_SHORT.YearFraction *BlackFormulaRepository.driftlessTheta(forward + shift, STRIKE + shift, expiry, volatility); assertEquals(computedCaplet.Currency, EUR); assertEquals(computedCaplet.Amount, expectedCaplet, NOTIONAL * TOL); assertEquals(computedFloorlet.Currency, EUR); assertEquals(computedFloorlet.Amount, expectedFloorlet, NOTIONAL * TOL); }
//------------------------------------------------------------------------- public virtual void test_presentValueSensitivityVolatility() { PointSensitivities pointCaplet = PRICER.presentValueSensitivityModelParamsSabr(CAPLET_LONG, RATES, VOLS).build(); PointSensitivities pointFloorlet = PRICER.presentValueSensitivityModelParamsSabr(FLOORLET_SHORT, RATES, VOLS).build(); double forward = RATES.iborIndexRates(EUR_EURIBOR_3M).rate(RATE_COMP.Observation); double expiry = VOLS.relativeTime(CAPLET_LONG.FixingDateTime); ValueDerivatives volSensi = VOLS.Parameters.volatilityAdjoint(expiry, STRIKE, forward); double df = RATES.discountFactor(EUR, CAPLET_LONG.PaymentDate); double vegaCaplet = NOTIONAL * df * CAPLET_LONG.YearFraction * BlackFormulaRepository.vega(forward + SHIFT, STRIKE + SHIFT, expiry, volSensi.Value); double vegaFloorlet = -NOTIONAL *df *CAPLET_LONG.YearFraction *BlackFormulaRepository.vega(forward + SHIFT, STRIKE + SHIFT, expiry, volSensi.Value); assertSensitivity(pointCaplet, SabrParameterType.ALPHA, vegaCaplet * volSensi.getDerivative(2), TOL); assertSensitivity(pointCaplet, SabrParameterType.BETA, vegaCaplet * volSensi.getDerivative(3), TOL); assertSensitivity(pointCaplet, SabrParameterType.RHO, vegaCaplet * volSensi.getDerivative(4), TOL); assertSensitivity(pointCaplet, SabrParameterType.NU, vegaCaplet * volSensi.getDerivative(5), TOL); assertSensitivity(pointFloorlet, SabrParameterType.ALPHA, vegaFloorlet * volSensi.getDerivative(2), TOL); assertSensitivity(pointFloorlet, SabrParameterType.BETA, vegaFloorlet * volSensi.getDerivative(3), TOL); assertSensitivity(pointFloorlet, SabrParameterType.RHO, vegaFloorlet * volSensi.getDerivative(4), TOL); assertSensitivity(pointFloorlet, SabrParameterType.NU, vegaFloorlet * volSensi.getDerivative(5), TOL); PointSensitivities pointCapletVol = PRICER.presentValueSensitivityModelParamsVolatility(CAPLET_LONG, RATES, VOLS).build(); // vol sensitivity in base class PointSensitivities pointFloorletVol = PRICER.presentValueSensitivityModelParamsVolatility(FLOORLET_SHORT, RATES, VOLS).build(); IborCapletFloorletSensitivity pointCapletVolExp = IborCapletFloorletSensitivity.of(VOLS.Name, expiry, STRIKE, forward, EUR, vegaCaplet); IborCapletFloorletSensitivity pointFloorletVolExp = IborCapletFloorletSensitivity.of(VOLS.Name, expiry, STRIKE, forward, EUR, vegaFloorlet); assertEquals(pointCapletVol.Sensitivities.get(0), pointCapletVolExp); assertEquals(pointFloorletVol.Sensitivities.get(0), pointFloorletVolExp); }
//------------------------------------------------------------------------- public virtual void test_presentValueSensitivityBlackVolatility() { FxOptionSensitivity computedCall = (FxOptionSensitivity)PRICER.presentValueSensitivityModelParamsVolatility(CALL_OTM, RATES_PROVIDER, VOLS); FxOptionSensitivity computedPut = (FxOptionSensitivity)PRICER.presentValueSensitivityModelParamsVolatility(PUT_ITM, RATES_PROVIDER, VOLS); double timeToExpiry = VOLS.relativeTime(EXPIRY); double df = RATES_PROVIDER.discountFactor(USD, PAYMENT_DATE); double forward = PRICER.DiscountingFxSingleProductPricer.forwardFxRate(FX_PRODUCT_HIGH, RATES_PROVIDER).fxRate(CURRENCY_PAIR); double vol = SMILE_TERM.volatility(timeToExpiry, STRIKE_RATE_HIGH, forward); FxOptionSensitivity expected = FxOptionSensitivity.of(VOLS.Name, CURRENCY_PAIR, timeToExpiry, STRIKE_RATE_HIGH, forward, USD, -NOTIONAL * df * BlackFormulaRepository.vega(forward, STRIKE_RATE_HIGH, timeToExpiry, vol)); assertTrue(computedCall.build().equalWithTolerance(expected.build(), NOTIONAL * TOL)); assertTrue(computedPut.build().equalWithTolerance(expected.build().multipliedBy(-1d), NOTIONAL * TOL)); }
//------------------------------------------------------------------------- public virtual void test_presentValue_formula() { CurrencyAmount computedCaplet = PRICER.presentValue(CAPLET_LONG, RATES, VOLS); CurrencyAmount computedFloorlet = PRICER.presentValue(FLOORLET_SHORT, RATES, VOLS); double forward = RATES.iborIndexRates(EUR_EURIBOR_3M).rate(RATE_COMP.Observation); double expiry = VOLS.relativeTime(CAPLET_LONG.FixingDateTime); double volatility = VOLS.volatility(expiry, STRIKE, forward); double df = RATES.discountFactor(EUR, CAPLET_LONG.PaymentDate); double expectedCaplet = NOTIONAL * df * CAPLET_LONG.YearFraction * BlackFormulaRepository.price(forward, STRIKE, expiry, volatility, true); double expectedFloorlet = -NOTIONAL *df *FLOORLET_SHORT.YearFraction *BlackFormulaRepository.price(forward, STRIKE, expiry, volatility, false); assertEquals(computedCaplet.Currency, EUR); assertEquals(computedCaplet.Amount, expectedCaplet, NOTIONAL * TOL); assertEquals(computedFloorlet.Currency, EUR); assertEquals(computedFloorlet.Amount, expectedFloorlet, NOTIONAL * TOL); }
//------------------------------------------------------------------------- public virtual void test_presentValue_formula() { CurrencyAmount computedCaplet = PRICER.presentValue(CAPLET_LONG, RATES, VOLS); CurrencyAmount computedFloorlet = PRICER.presentValue(FLOORLET_SHORT, RATES, VOLS); double forward = RATES.iborIndexRates(EUR_EURIBOR_3M).rate(RATE_COMP.Observation); double expiry = VOLS.relativeTime(CAPLET_LONG.FixingDateTime); double volatility = VOLS.volatility(expiry, STRIKE, forward); double df = RATES.discountFactor(EUR, CAPLET_LONG.PaymentDate); double expectedCaplet = NOTIONAL * df * CAPLET_LONG.YearFraction * BlackFormulaRepository.price(forward + SHIFT, STRIKE + SHIFT, expiry, volatility, CALL.Call); double expectedFloorlet = -NOTIONAL *df *FLOORLET_SHORT.YearFraction *BlackFormulaRepository.price(forward + SHIFT, STRIKE + SHIFT, expiry, volatility, PUT.Call); assertEquals(computedCaplet.Currency, EUR); assertEquals(computedCaplet.Amount, expectedCaplet, NOTIONAL * TOL); assertEquals(computedFloorlet.Currency, EUR); assertEquals(computedFloorlet.Amount, expectedFloorlet, NOTIONAL * TOL); // consistency with shifted Black ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities vols = ShiftedBlackIborCapletFloorletExpiryStrikeVolatilities.of(EUR_EURIBOR_3M, VALUATION, ConstantSurface.of("constVol", volatility).withMetadata(Surfaces.blackVolatilityByExpiryStrike("costVol", DayCounts.ACT_ACT_ISDA)), IborCapletFloorletSabrRateVolatilityDataSet.CURVE_CONST_SHIFT); CurrencyAmount computedCapletBlack = PRICER_BASE.presentValue(CAPLET_LONG, RATES, vols); CurrencyAmount computedFloorletBlack = PRICER_BASE.presentValue(FLOORLET_SHORT, RATES, vols); assertEquals(computedCaplet.Amount, computedCapletBlack.Amount, NOTIONAL * TOL); assertEquals(computedFloorlet.Amount, computedFloorletBlack.Amount, NOTIONAL * TOL); }