/// <summary>
        /// Calculates the gamma of the bond future option product based on the price of the underlying future.
        /// <para>
        /// The gamma of the product is the sensitivity of the option delta 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 gammaStickyStrike(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             gamma        = BlackFormulaRepository.gamma(futurePrice, strike, timeToExpiry, volatility);

            return(gamma);
        }
        //-------------------------------------------------------------------------
        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_presentValueGamma_formula_shift()
        {
            CurrencyAmount computedCaplet   = PRICER.presentValueGamma(CAPLET_LONG, RATES, SHIFTED_VOLS);
            CurrencyAmount computedFloorlet = PRICER.presentValueGamma(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.gamma(forward + shift, STRIKE + shift, expiry, volatility);
            double         expectedFloorlet = -NOTIONAL *df *FLOORLET_SHORT.YearFraction *BlackFormulaRepository.gamma(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);
        }