コード例 #1
0
 //-------------------------------------------------------------------------
 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))));
        }
コード例 #4
0
 //-------------------------------------------------------------------------
 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);
        }
コード例 #8
0
        /// <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");
        }
コード例 #9
0
        //-------------------------------------------------------------------------
        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);
        }
コード例 #11
0
        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
            }
        }
コード例 #12
0
        //-------------------------------------------------------------------------
        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);
        }