//-------------------------------------------------------------------------
        /// <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 delta of the bond future option product based on the price of the underlying future.
        /// <para>
        /// The delta of the product is the sensitivity of the option price to the future price.
        /// 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 deltaStickyStrike(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             delta        = BlackFormulaRepository.delta(futurePrice, strike, timeToExpiry, volatility, futureOption.PutCall.Call);

            return(delta);
        }
        //-------------------------------------------------------------------------
        public virtual void test_delta_presentValueDelta()
        {
            double         deltaCall           = PRICER.delta(CALL_OTM, RATES_PROVIDER, VOLS);
            CurrencyAmount pvDeltaCall         = PRICER.presentValueDelta(CALL_OTM, RATES_PROVIDER, VOLS);
            double         deltaPut            = PRICER.delta(PUT_ITM, RATES_PROVIDER, VOLS);
            CurrencyAmount pvDeltaPut          = PRICER.presentValueDelta(PUT_ITM, RATES_PROVIDER, VOLS);
            double         timeToExpiry        = VOLS.relativeTime(EXPIRY);
            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         expectedDeltaCall   = dfFor * BlackFormulaRepository.delta(forward, STRIKE_RATE_HIGH, timeToExpiry, vol, true);
            double         expectedDeltaPut    = dfFor * BlackFormulaRepository.delta(forward, STRIKE_RATE_HIGH, timeToExpiry, vol, false);
            double         expectedPvDeltaCall = -NOTIONAL *dfFor *BlackFormulaRepository.delta(forward, STRIKE_RATE_HIGH, timeToExpiry, vol, true);

            double expectedPvDeltaPut = NOTIONAL * dfFor * BlackFormulaRepository.delta(forward, STRIKE_RATE_HIGH, timeToExpiry, vol, false);

            assertEquals(deltaCall, expectedDeltaCall, TOL);
            assertEquals(pvDeltaCall.Currency, USD);
            assertEquals(pvDeltaCall.Amount, expectedPvDeltaCall, NOTIONAL * TOL);
            assertEquals(deltaPut, expectedDeltaPut, TOL);
            assertEquals(pvDeltaPut.Currency, USD);
            assertEquals(pvDeltaPut.Amount, expectedPvDeltaPut, NOTIONAL * TOL);
        }
        public virtual void test_presentValueDelta_formula_shift()
        {
            CurrencyAmount computedCaplet   = PRICER.presentValueDelta(CAPLET_LONG, RATES, SHIFTED_VOLS);
            CurrencyAmount computedFloorlet = PRICER.presentValueDelta(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.delta(forward + shift, STRIKE + shift, expiry, volatility, true);
            double         expectedFloorlet = -NOTIONAL *df *FLOORLET_SHORT.YearFraction *BlackFormulaRepository.delta(forward + shift, STRIKE + shift, expiry, volatility, false);

            assertEquals(computedCaplet.Currency, EUR);
            assertEquals(computedCaplet.Amount, expectedCaplet, NOTIONAL * TOL);
            assertEquals(computedFloorlet.Currency, EUR);
            assertEquals(computedFloorlet.Amount, expectedFloorlet, NOTIONAL * TOL);
        }