//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_monotonicity()
        public virtual void test_monotonicity()
        {
            double priceDkoPrev = 100d;
            double priceDkiPrev = 0d;
            double priceUkoPrev = 0d;
            double priceUkiPrev = 100d;

            for (int i = 0; i < 50; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.006 * i;
                SimpleConstantContinuousBarrier dko       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDko = ResolvedFxSingleBarrierOption.of(CALL, dko);
                double priceDko = PRICER_39.price(optionDko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier dki       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDki = ResolvedFxSingleBarrierOption.of(CALL, dki);
                double priceDki = PRICER_39.price(optionDki, RATE_PROVIDER, VOLS, DATA_39);
                // down barrier
                double higherBarrier = 1.4 + 0.006 * (i + 1);
                SimpleConstantContinuousBarrier uko       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUko = ResolvedFxSingleBarrierOption.of(CALL, uko);
                double priceUko = PRICER_39.price(optionUko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier uki       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUki = ResolvedFxSingleBarrierOption.of(CALL, uki);
                double priceUki = PRICER_39.price(optionUki, RATE_PROVIDER, VOLS, DATA_39);
                assertTrue(priceDkoPrev > priceDko);
                assertTrue(priceDkiPrev < priceDki);
                assertTrue(priceUkoPrev < priceUko);
                assertTrue(priceUkiPrev > priceUki);
                priceDkoPrev = priceDko;
                priceDkiPrev = priceDki;
                priceUkoPrev = priceUko;
                priceUkiPrev = priceUki;
            }
        }
        /// <summary>
        /// Calculates the present value sensitivity of the FX barrier option product.
        /// <para>
        /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to
        /// the underlying curve parameters.
        /// </para>
        /// <para>
        /// The sensitivity is computed by bump and re-price.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <param name="baseTreeData">  the trinomial tree data </param>
        /// <returns> the present value of the product </returns>
        public virtual CurrencyParameterSensitivities presentValueSensitivityRates(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData baseTreeData)
        {
            ArgChecker.isTrue(baseTreeData.NumberOfSteps == calibrator.NumberOfSteps, "the number of steps mismatch between pricer and trinomial tree data");
            double                         shift            = 1.0e-5;
            CurrencyAmount                 pvBase           = presentValue(option, ratesProvider, volatilities, baseTreeData);
            ResolvedFxVanillaOption        underlyingOption = option.UnderlyingOption;
            ResolvedFxSingle               underlyingFx     = underlyingOption.Underlying;
            CurrencyPair                   currencyPair     = underlyingFx.CurrencyPair;
            ImmutableRatesProvider         immRatesProvider = ratesProvider.toImmutableRatesProvider();
            ImmutableMap <Currency, Curve> baseCurves       = immRatesProvider.DiscountCurves;
            CurrencyParameterSensitivities result           = CurrencyParameterSensitivities.empty();

            foreach (KeyValuePair <Currency, Curve> entry in baseCurves.entrySet())
            {
                if (currencyPair.contains(entry.Key))
                {
                    Curve       curve       = entry.Value;
                    int         nParams     = curve.ParameterCount;
                    DoubleArray sensitivity = DoubleArray.of(nParams, i =>
                    {
                        Curve dscBumped = curve.withParameter(i, curve.getParameter(i) + shift);
                        IDictionary <Currency, Curve> mapBumped = new Dictionary <Currency, Curve>(baseCurves);
                        mapBumped[entry.Key] = dscBumped;
                        ImmutableRatesProvider providerDscBumped = immRatesProvider.toBuilder().discountCurves(mapBumped).build();
                        double pvBumped = presentValue(option, providerDscBumped, volatilities).Amount;
                        return((pvBumped - pvBase.Amount) / shift);
                    });
                    result = result.combinedWith(curve.createParameterSensitivity(pvBase.Currency, sensitivity));
                }
            }
            return(result);
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value theta of the FX barrier option product.
        /// <para>
        /// The present value theta is the negative of the first derivative of <seealso cref="#presentValue"/> with time parameter.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the present value theta of the product </returns>
        public virtual CurrencyAmount presentValueTheta(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            double theta = this.theta(option, ratesProvider, volatilities);
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;

            return(CurrencyAmount.of(underlyingOption.CounterCurrency, signedNotional(underlyingOption) * theta));
        }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_black()
        public virtual void test_black()
        {
            double tol = 1.0e-2;

            for (int i = 0; i < 11; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier dko       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDko = ResolvedFxSingleBarrierOption.of(CALL, dko);
                double priceDkoBlack = BLACK_PRICER.price(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceDko      = PRICER_70.price(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceDko, priceDkoBlack, tol);
                SimpleConstantContinuousBarrier dki       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDki = ResolvedFxSingleBarrierOption.of(CALL, dki);
                double priceDkiBlack = BLACK_PRICER.price(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceDki      = PRICER_70.price(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceDki, priceDkiBlack, tol);
                // down barrier
                double higherBarrier = 1.45 + 0.025 * i;
                SimpleConstantContinuousBarrier uko       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUko = ResolvedFxSingleBarrierOption.of(CALL, uko);
                double priceUkoBlack = BLACK_PRICER.price(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceUko      = PRICER_70.price(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceUko, priceUkoBlack, tol);
                SimpleConstantContinuousBarrier uki       = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUki = ResolvedFxSingleBarrierOption.of(CALL, uki);
                double priceUkiBlack = BLACK_PRICER.price(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                double priceUki      = PRICER_70.price(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEqualsRelative(priceUki, priceUkiBlack, tol);
            }
        }
        //-------------------------------------------------------------------------
        private void validateData(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData data)
        {
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;

            ArgChecker.isTrue(DoubleMath.fuzzyEquals(data.getTime(data.NumberOfSteps), volatilities.relativeTime(underlyingOption.Expiry), SMALL), "time to expiry mismatch between pricing option and trinomial tree data");
            ArgChecker.isTrue(DoubleMath.fuzzyEquals(data.Spot, ratesProvider.fxRate(underlyingOption.Underlying.CurrencyPair), SMALL), "today's FX rate mismatch between rates provider and trinomial tree data");
        }
        /// <summary>
        /// Calculates the present value of the FX barrier option product.
        /// <para>
        /// The present value of the product is the value on the valuation date.
        /// It is expressed in the counter currency.
        /// </para>
        /// <para>
        /// This assumes the tree is already calibrated and the tree data is stored as {@code RecombiningTrinomialTreeData}.
        /// The tree data should be consistent with the pricer and other inputs, see <seealso cref="#validateData"/>.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <param name="treeData">  the trinomial tree data </param>
        /// <returns> the present value of the product </returns>
        public virtual CurrencyAmount presentValue(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData treeData)
        {
            double price = this.price(option, ratesProvider, volatilities, treeData);
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;

            return(CurrencyAmount.of(underlyingOption.CounterCurrency, signedNotional(underlyingOption) * price));
        }
        //-------------------------------------------------------------------------
        private ValueDerivatives priceDerivatives(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData data)
        {
            validate(option, ratesProvider, volatilities);
            validateData(option, ratesProvider, volatilities, data);
            int nSteps = data.NumberOfSteps;
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;
            double           timeToExpiry            = data.getTime(nSteps);
            ResolvedFxSingle underlyingFx            = underlyingOption.Underlying;
            Currency         ccyBase                  = underlyingFx.CounterCurrencyPayment.Currency;
            Currency         ccyCounter               = underlyingFx.CounterCurrencyPayment.Currency;
            DiscountFactors  baseDiscountFactors      = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors   = ratesProvider.discountFactors(ccyCounter);
            double           rebateAtExpiry           = 0d; // used to price knock-in option
            double           rebateAtExpiryDerivative = 0d; // used to price knock-in option
            double           notional                 = Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);

            double[] rebateArray = new double[nSteps + 1];
            SimpleConstantContinuousBarrier barrier = (SimpleConstantContinuousBarrier)option.Barrier;

            if (option.Rebate.Present)
            {
                CurrencyAmount rebateCurrencyAmount = option.Rebate.get();
                double         rebatePerUnit        = rebateCurrencyAmount.Amount / notional;
                bool           isCounter            = rebateCurrencyAmount.Currency.Equals(ccyCounter);
                double         rebate = isCounter ? rebatePerUnit : rebatePerUnit * barrier.BarrierLevel;
                if (barrier.KnockType.KnockIn)
                {   // use in-out parity
                    double dfCounterAtExpiry = counterDiscountFactors.discountFactor(timeToExpiry);
                    double dfBaseAtExpiry    = baseDiscountFactors.discountFactor(timeToExpiry);
                    for (int i = 0; i < nSteps + 1; ++i)
                    {
                        rebateArray[i] = isCounter ? rebate * dfCounterAtExpiry / counterDiscountFactors.discountFactor(data.getTime(i)) : rebate * dfBaseAtExpiry / baseDiscountFactors.discountFactor(data.getTime(i));
                    }
                    if (isCounter)
                    {
                        rebateAtExpiry = rebatePerUnit * dfCounterAtExpiry;
                    }
                    else
                    {
                        rebateAtExpiry           = rebatePerUnit * data.Spot * dfBaseAtExpiry;
                        rebateAtExpiryDerivative = rebatePerUnit * dfBaseAtExpiry;
                    }
                }
                else
                {
                    Arrays.fill(rebateArray, rebate);
                }
            }
            ConstantContinuousSingleBarrierKnockoutFunction barrierFunction = ConstantContinuousSingleBarrierKnockoutFunction.of(underlyingOption.Strike, timeToExpiry, underlyingOption.PutCall, nSteps, barrier.BarrierType, barrier.BarrierLevel, DoubleArray.ofUnsafe(rebateArray));
            ValueDerivatives barrierPrice = TREE.optionPriceAdjoint(barrierFunction, data);

            if (barrier.KnockType.KnockIn)
            {     // use in-out parity
                EuropeanVanillaOptionFunction vanillaFunction = EuropeanVanillaOptionFunction.of(underlyingOption.Strike, timeToExpiry, underlyingOption.PutCall, nSteps);
                ValueDerivatives vanillaPrice = TREE.optionPriceAdjoint(vanillaFunction, data);
                return(ValueDerivatives.of(vanillaPrice.Value + rebateAtExpiry - barrierPrice.Value, DoubleArray.of(vanillaPrice.getDerivative(0) + rebateAtExpiryDerivative - barrierPrice.getDerivative(0))));
            }
            return(barrierPrice);
        }
        /// <summary>
        /// Calculates the delta of the FX barrier option product.
        /// <para>
        /// The delta is the first derivative of <seealso cref="#price"/> with respect to spot.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the delta of the product </returns>
        public virtual double delta(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            if (volatilities.relativeTime(option.UnderlyingOption.Expiry) < 0d)
            {
                return(0d);
            }
            ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities);

            return(priceDerivatives.getDerivative(0));
        }
        /// <summary>
        /// Calculates the currency exposure of the FX barrier option product.
        /// <para>
        /// This assumes the tree is already calibrated and the tree data is stored as {@code RecombiningTrinomialTreeData}.
        /// The tree data should be consistent with the pricer and other inputs, see <seealso cref="#validateData"/>.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <param name="treeData">  the trinomial tree data </param>
        /// <returns> the currency exposure </returns>
        public virtual MultiCurrencyAmount currencyExposure(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData treeData)
        {
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;
            ValueDerivatives        priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities, treeData);
            double         price          = priceDerivatives.Value;
            double         delta          = priceDerivatives.getDerivative(0);
            CurrencyPair   currencyPair   = underlyingOption.Underlying.CurrencyPair;
            double         todayFx        = ratesProvider.fxRate(currencyPair);
            double         signedNotional = this.signedNotional(underlyingOption);
            CurrencyAmount domestic       = CurrencyAmount.of(currencyPair.Counter, (price - delta * todayFx) * signedNotional);
            CurrencyAmount foreign        = CurrencyAmount.of(currencyPair.Base, delta * signedNotional);

            return(MultiCurrencyAmount.of(domestic, foreign));
        }
        public virtual void test_tradePricer()
        {
            for (int i = 0; i < 11; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier    dko       = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption      callDko   = ResolvedFxSingleBarrierOption.of(CALL, dko);
                ResolvedFxSingleBarrierOptionTrade callTrade = ResolvedFxSingleBarrierOptionTrade.builder().product(callDko).premium(Payment.of(EUR, 0, VAL_DATE)).build();

                CurrencyAmount      pvProduct = PRICER_39.presentValue(callDko, RATE_PROVIDER, VOLS);
                MultiCurrencyAmount pvTrade   = TRADE_PRICER_39.presentValue(callTrade, RATE_PROVIDER, VOLS);
                assertEquals(pvTrade.getAmount(USD), pvProduct);
            }
        }
        //-------------------------------------------------------------------------
        //  The derivatives are [0] spot, [1] strike, [2] rate, [3] cost-of-carry, [4] volatility, [5] timeToExpiry, [6] spot twice
        private ValueDerivatives priceDerivatives(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            validate(option, ratesProvider, volatilities);
            SimpleConstantContinuousBarrier barrier          = (SimpleConstantContinuousBarrier)option.Barrier;
            ResolvedFxVanillaOption         underlyingOption = option.UnderlyingOption;

            double[] derivatives = new double[7];
            if (volatilities.relativeTime(underlyingOption.Expiry) < 0d)
            {
                return(ValueDerivatives.of(0d, DoubleArray.ofUnsafe(derivatives)));
            }
            ResolvedFxSingle underlyingFx           = underlyingOption.Underlying;
            CurrencyPair     currencyPair           = underlyingFx.CurrencyPair;
            Currency         ccyBase                = currencyPair.Base;
            Currency         ccyCounter             = currencyPair.Counter;
            DiscountFactors  baseDiscountFactors    = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors = ratesProvider.discountFactors(ccyCounter);

            double           rateBase         = baseDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double           rateCounter      = counterDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double           costOfCarry      = rateCounter - rateBase;
            double           dfBase           = baseDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double           dfCounter        = counterDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double           todayFx          = ratesProvider.fxRate(currencyPair);
            double           strike           = underlyingOption.Strike;
            double           forward          = todayFx * dfBase / dfCounter;
            double           volatility       = volatilities.volatility(currencyPair, underlyingOption.Expiry, strike, forward);
            double           timeToExpiry     = volatilities.relativeTime(underlyingOption.Expiry);
            ValueDerivatives valueDerivatives = BARRIER_PRICER.priceAdjoint(todayFx, strike, timeToExpiry, costOfCarry, rateCounter, volatility, underlyingOption.PutCall.Call, barrier);

            if (!option.Rebate.Present)
            {
                return(valueDerivatives);
            }
            CurrencyAmount   rebate = option.Rebate.get();
            ValueDerivatives valueDerivativesRebate = rebate.Currency.Equals(ccyCounter) ? CASH_REBATE_PRICER.priceAdjoint(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType()) : ASSET_REBATE_PRICER.priceAdjoint(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType());
            double           rebateRate             = rebate.Amount / Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);
            double           price = valueDerivatives.Value + rebateRate * valueDerivativesRebate.Value;

            derivatives[0] = valueDerivatives.getDerivative(0) + rebateRate * valueDerivativesRebate.getDerivative(0);
            derivatives[1] = valueDerivatives.getDerivative(1);
            for (int i = 2; i < 7; ++i)
            {
                derivatives[i] = valueDerivatives.getDerivative(i) + rebateRate * valueDerivativesRebate.getDerivative(i - 1);
            }
            return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(derivatives)));
        }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_inOutParity()
        public virtual void test_inOutParity()
        {
            double tol       = 1.0e-2;
            double callPrice = VANILLA_PRICER.price(CALL, RATE_PROVIDER, VOLS);
            double putPrice  = VANILLA_PRICER.price(PUT, RATE_PROVIDER, VOLS);

            for (int i = 0; i < 11; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier dko     = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   callDko = ResolvedFxSingleBarrierOption.of(CALL, dko);
                double priceCallDko = PRICER_39.price(callDko, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putDko = ResolvedFxSingleBarrierOption.of(PUT, dko);
                double pricePutDko = PRICER_39.price(putDko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier dki     = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   callDki = ResolvedFxSingleBarrierOption.of(CALL, dki);
                double priceCallDki = PRICER_39.price(callDki, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putDki = ResolvedFxSingleBarrierOption.of(PUT, dki);
                double pricePutDki = PRICER_39.price(putDki, RATE_PROVIDER, VOLS, DATA_39);
                assertEqualsRelative(priceCallDko + priceCallDki, callPrice, tol);
                assertEqualsRelative(pricePutDko + pricePutDki, putPrice, tol);
                // down barrier
                double higherBarrier = 1.45 + 0.025 * i;
                SimpleConstantContinuousBarrier uko     = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   callUko = ResolvedFxSingleBarrierOption.of(CALL, uko);
                double priceCallUko = PRICER_39.price(callUko, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putUko = ResolvedFxSingleBarrierOption.of(PUT, uko);
                double pricePutUko = PRICER_39.price(putUko, RATE_PROVIDER, VOLS, DATA_39);
                SimpleConstantContinuousBarrier uki     = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   callUki = ResolvedFxSingleBarrierOption.of(CALL, uki);
                double priceCallUki = PRICER_39.price(callUki, RATE_PROVIDER, VOLS, DATA_39);
                ResolvedFxSingleBarrierOption putUki = ResolvedFxSingleBarrierOption.of(PUT, uki);
                double pricePutUki = PRICER_39.price(putUki, RATE_PROVIDER, VOLS, DATA_39);
                assertEqualsRelative(priceCallUko + priceCallUki, callPrice, tol);
                assertEqualsRelative(pricePutUko + pricePutUki, putPrice, tol);
            }
        }
        /// <summary>
        /// Calculates the price of the FX barrier option product.
        /// <para>
        /// The price of the product is the value on the valuation date for one unit of the base currency
        /// and is expressed in the counter currency. The price does not take into account the long/short flag.
        /// See <seealso cref="#presentValue"/> for scaling and currency.
        /// </para>
        /// <para>
        /// The volatility used in this computation is the Black implied volatility at expiry time and strike.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the price of the product </returns>
        public virtual double price(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            validate(option, ratesProvider, volatilities);
            SimpleConstantContinuousBarrier barrier          = (SimpleConstantContinuousBarrier)option.Barrier;
            ResolvedFxVanillaOption         underlyingOption = option.UnderlyingOption;

            if (volatilities.relativeTime(underlyingOption.Expiry) < 0d)
            {
                return(0d);
            }
            ResolvedFxSingle underlyingFx           = underlyingOption.Underlying;
            Currency         ccyBase                = underlyingFx.BaseCurrencyPayment.Currency;
            Currency         ccyCounter             = underlyingFx.CounterCurrencyPayment.Currency;
            CurrencyPair     currencyPair           = underlyingFx.CurrencyPair;
            DiscountFactors  baseDiscountFactors    = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors = ratesProvider.discountFactors(ccyCounter);

            double rateBase     = baseDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double rateCounter  = counterDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double costOfCarry  = rateCounter - rateBase;
            double dfBase       = baseDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double dfCounter    = counterDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double todayFx      = ratesProvider.fxRate(currencyPair);
            double strike       = underlyingOption.Strike;
            double forward      = todayFx * dfBase / dfCounter;
            double volatility   = volatilities.volatility(currencyPair, underlyingOption.Expiry, strike, forward);
            double timeToExpiry = volatilities.relativeTime(underlyingOption.Expiry);
            double price        = BARRIER_PRICER.price(todayFx, strike, timeToExpiry, costOfCarry, rateCounter, volatility, underlyingOption.PutCall.Call, barrier);

            if (option.Rebate.Present)
            {
                CurrencyAmount rebate      = option.Rebate.get();
                double         priceRebate = rebate.Currency.Equals(ccyCounter) ? CASH_REBATE_PRICER.price(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType()) : ASSET_REBATE_PRICER.price(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType());
                price += priceRebate * rebate.Amount / Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);
            }
            return(price);
        }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void test_black_currencyExposure()
        public virtual void test_black_currencyExposure()
        {
            double tol = 7.0e-2;     // large tol due to approximated delta

            for (int i = 0; i < 8; ++i)
            {
                // up barrier
                double lowerBarrier = 1.1 + 0.025 * i;
                SimpleConstantContinuousBarrier dko        = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDko  = ResolvedFxSingleBarrierOption.of(CALL, dko, REBATE_BASE);
                MultiCurrencyAmount             ceDkoBlack = BLACK_PRICER.currencyExposure(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceDko      = PRICER_70.currencyExposure(optionDko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceDko.getAmount(EUR).Amount, ceDkoBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceDko.getAmount(USD).Amount, ceDkoBlack.getAmount(USD).Amount, NOTIONAL * tol);
                SimpleConstantContinuousBarrier dki        = SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_IN, lowerBarrier);
                ResolvedFxSingleBarrierOption   optionDki  = ResolvedFxSingleBarrierOption.of(CALL, dki, REBATE);
                MultiCurrencyAmount             ceDkiBlack = BLACK_PRICER.currencyExposure(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceDki      = PRICER_70.currencyExposure(optionDki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceDki.getAmount(EUR).Amount, ceDkiBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceDki.getAmount(USD).Amount, ceDkiBlack.getAmount(USD).Amount, NOTIONAL * tol);
                // down barrier
                double higherBarrier = 1.45 + 0.025 * i;
                SimpleConstantContinuousBarrier uko        = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_OUT, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUko  = ResolvedFxSingleBarrierOption.of(CALL, uko, REBATE);
                MultiCurrencyAmount             ceUkoBlack = BLACK_PRICER.currencyExposure(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceUko      = PRICER_70.currencyExposure(optionUko, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceUko.getAmount(EUR).Amount, ceUkoBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceUko.getAmount(USD).Amount, ceUkoBlack.getAmount(USD).Amount, NOTIONAL * tol);
                SimpleConstantContinuousBarrier uki        = SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, higherBarrier);
                ResolvedFxSingleBarrierOption   optionUki  = ResolvedFxSingleBarrierOption.of(CALL, uki, REBATE_BASE);
                MultiCurrencyAmount             ceUkiBlack = BLACK_PRICER.currencyExposure(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT);
                MultiCurrencyAmount             ceUki      = PRICER_70.currencyExposure(optionUki, RATE_PROVIDER_FLAT, VOLS_FLAT, DATA_70_FLAT);
                assertEquals(ceUki.getAmount(EUR).Amount, ceUkiBlack.getAmount(EUR).Amount, NOTIONAL * tol);
                assertEquals(ceUki.getAmount(USD).Amount, ceUkiBlack.getAmount(USD).Amount, NOTIONAL * tol);
            }
        }
 private void validate(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
 {
     ArgChecker.isTrue(option.Barrier is SimpleConstantContinuousBarrier, "barrier should be SimpleConstantContinuousBarrier");
     ArgChecker.isTrue(ratesProvider.ValuationDate.isEqual(volatilities.ValuationDateTime.toLocalDate()), "Volatility and rate data must be for the same date");
 }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Computes the present value sensitivity to the black volatility used in the pricing.
        /// <para>
        /// The result is a single sensitivity to the volatility used. This is also called Black vega.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the present value sensitivity </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityModelParamsVolatility(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;

            if (volatilities.relativeTime(underlyingOption.Expiry) <= 0d)
            {
                return(PointSensitivityBuilder.none());
            }
            ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities);
            ResolvedFxSingle underlyingFx     = underlyingOption.Underlying;
            CurrencyPair     currencyPair     = underlyingFx.CurrencyPair;
            Currency         ccyBase          = currencyPair.Base;
            Currency         ccyCounter       = currencyPair.Counter;
            double           dfBase           = ratesProvider.discountFactor(ccyBase, underlyingFx.PaymentDate);
            double           dfCounter        = ratesProvider.discountFactor(ccyCounter, underlyingFx.PaymentDate);
            double           todayFx          = ratesProvider.fxRate(currencyPair);
            double           forward          = todayFx * dfBase / dfCounter;

            return(FxOptionSensitivity.of(volatilities.Name, currencyPair, volatilities.relativeTime(underlyingOption.Expiry), underlyingOption.Strike, forward, ccyCounter, priceDerivatives.getDerivative(4) * signedNotional(underlyingOption)));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the FX barrier option product.
        /// <para>
        /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to
        /// the underlying curves.
        /// </para>
        /// <para>
        /// The volatility is fixed in this sensitivity computation, i.e., sticky-strike.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the present value curve sensitivity of the product </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;

            if (volatilities.relativeTime(underlyingOption.Expiry) <= 0d)
            {
                return(PointSensitivityBuilder.none());
            }
            ValueDerivatives    priceDerivatives    = this.priceDerivatives(option, ratesProvider, volatilities);
            ResolvedFxSingle    underlyingFx        = underlyingOption.Underlying;
            CurrencyPair        currencyPair        = underlyingFx.CurrencyPair;
            double              signedNotional      = this.signedNotional(underlyingOption);
            double              counterYearFraction = ratesProvider.discountFactors(currencyPair.Counter).relativeYearFraction(underlyingFx.PaymentDate);
            ZeroRateSensitivity counterSensi        = ZeroRateSensitivity.of(currencyPair.Counter, counterYearFraction, signedNotional * (priceDerivatives.getDerivative(2) + priceDerivatives.getDerivative(3)));
            double              baseYearFraction    = ratesProvider.discountFactors(currencyPair.Base).relativeYearFraction(underlyingFx.PaymentDate);
            ZeroRateSensitivity baseSensi           = ZeroRateSensitivity.of(currencyPair.Base, baseYearFraction, currencyPair.Counter, -priceDerivatives.getDerivative(3) * signedNotional);

            return(counterSensi.combinedWith(baseSensi));
        }
        /// <summary>
        /// Calculates the theta of the FX barrier option product.
        /// <para>
        /// The theta is the negative of the first derivative of <seealso cref="#price"/> with respect to time parameter.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the theta of the product </returns>
        public virtual double theta(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities);

            return(-priceDerivatives.getDerivative(5));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the currency exposure of the FX barrier option product.
        /// <para>
        /// The trinomial tree is first calibrated to Black volatilities,
        /// then the price is computed based on the calibrated tree.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the currency exposure </returns>
        public virtual MultiCurrencyAmount currencyExposure(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            RecombiningTrinomialTreeData treeData = calibrator.calibrateTrinomialTree(option.UnderlyingOption, ratesProvider, volatilities);

            return(currencyExposure(option, ratesProvider, volatilities, treeData));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the price of the FX barrier option product.
        /// <para>
        /// The price of the product is the value on the valuation date for one unit of the base currency
        /// and is expressed in the counter currency. The price does not take into account the long/short flag.
        /// See <seealso cref="#presentValue(ResolvedFxSingleBarrierOption, RatesProvider, BlackFxOptionVolatilities) presentValue"/>
        /// for scaling and currency.
        /// </para>
        /// <para>
        /// The trinomial tree is first calibrated to Black volatilities,
        /// then the price is computed based on the calibrated tree.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the price of the product </returns>
        public virtual double price(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            RecombiningTrinomialTreeData treeData = calibrator.calibrateTrinomialTree(option.UnderlyingOption, ratesProvider, volatilities);

            return(price(option, ratesProvider, volatilities, treeData));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the FX barrier option product.
        /// <para>
        /// The present value sensitivity of the product is the sensitivity of <seealso cref="#presentValue"/> to
        /// the underlying curve parameters.
        /// </para>
        /// <para>
        /// The sensitivity is computed by bump and re-price.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the present value of the product </returns>
        public virtual CurrencyParameterSensitivities presentValueSensitivityRates(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            RecombiningTrinomialTreeData baseTreeData = calibrator.calibrateTrinomialTree(option.UnderlyingOption, ratesProvider, volatilities);

            return(presentValueSensitivityRates(option, ratesProvider, volatilities, baseTreeData));
        }
 /// <summary>
 /// Calculates the price of the FX barrier option product.
 /// <para>
 /// The price of the product is the value on the valuation date for one unit of the base currency
 /// and is expressed in the counter currency. The price does not take into account the long/short flag.
 /// See <seealso cref="#presentValue(ResolvedFxSingleBarrierOption, RatesProvider, BlackFxOptionVolatilities, RecombiningTrinomialTreeData) presnetValue"/>
 /// for scaling and currency.
 /// </para>
 /// <para>
 /// This assumes the tree is already calibrated and the tree data is stored as {@code RecombiningTrinomialTreeData}.
 /// The tree data should be consistent with the pricer and other inputs, see <seealso cref="#validateData"/>.
 ///
 /// </para>
 /// </summary>
 /// <param name="option">  the option product </param>
 /// <param name="ratesProvider">  the rates provider </param>
 /// <param name="volatilities">  the Black volatility provider </param>
 /// <param name="treeData">  the trinomial tree data </param>
 /// <returns> the price of the product </returns>
 public virtual double price(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData treeData)
 {
     return(priceDerivatives(option, ratesProvider, volatilities, treeData).Value);
 }