예제 #1
0
        /// <summary>
        /// Computes the price of a one-touch/no-touch option.
        /// </summary>
        /// <param name="spot">  the spot </param>
        /// <param name="timeToExpiry">  the time to expiry </param>
        /// <param name="costOfCarry">  the cost of carry </param>
        /// <param name="rate">  the interest rate </param>
        /// <param name="lognormalVol">  the lognormal volatility </param>
        /// <param name="barrier">  the barrier </param>
        /// <returns> the price </returns>
        public virtual double price(double spot, double timeToExpiry, double costOfCarry, double rate, double lognormalVol, SimpleConstantContinuousBarrier barrier)
        {
            ArgChecker.notNull(barrier, "barrier");
            bool   isKnockIn = barrier.KnockType.KnockIn;
            bool   isDown    = barrier.BarrierType.Down;
            double h         = barrier.BarrierLevel;

            ArgChecker.isFalse(isDown && spot <= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (DOWN and spot<=barrier).");
            ArgChecker.isFalse(!isDown && spot >= barrier.BarrierLevel, "The Data is not consistent with an alive barrier (UP and spot>=barrier).");
            double eta            = isDown ? 1 : -1;
            double df2            = Math.Exp(-rate * timeToExpiry);
            double lognormalVolSq = lognormalVol * lognormalVol;
            double lognormalVolT  = lognormalVol * Math.Sqrt(timeToExpiry);

            if (DoubleMath.fuzzyEquals(Math.Min(timeToExpiry, lognormalVolSq), 0d, SMALL))
            {
                return(isKnockIn ? 0d : df2);
            }
            double mu     = (costOfCarry - 0.5 * lognormalVolSq) / lognormalVolSq;
            double lambda = Math.Sqrt(mu * mu + 2 * rate / lognormalVolSq);
            double m1     = lognormalVolT * (1 + mu);
            double x2     = Math.Log(spot / h) / lognormalVolT + m1;
            double y2     = Math.Log(h / spot) / lognormalVolT + m1;
            double z      = Math.Log(h / spot) / lognormalVolT + lambda * lognormalVolT;
            double xE     = isKnockIn ? getF(spot, z, lognormalVolT, h, mu, lambda, eta) : getE(spot, df2, x2, y2, lognormalVolT, h, mu, eta);

            return(xE);
        }
 /// <summary>
 /// {@inheritDoc} </summary>
 /// <exception cref="MathException"> If there are no real roots; if the Commons method could not evaluate the function; if the Commons method could not converge. </exception>
 public virtual double?[] getRoots(RealPolynomialFunction1D function)
 {
     ArgChecker.notNull(function, "function");
     try
     {
         Complex[]      roots     = ROOT_FINDER.solveAllComplex(function.Coefficients, 0);
         IList <double> realRoots = new List <double>();
         foreach (Complex c in roots)
         {
             if (DoubleMath.fuzzyEquals(c.Imaginary, 0d, EPS))
             {
                 realRoots.Add(c.Real);
             }
         }
         if (realRoots.Count == 0)
         {
             throw new MathException("Could not find any real roots");
         }
         return(realRoots.ToArray());
     }
     catch (TooManyEvaluationsException e)
     {
         throw new MathException(e);
     }
 }
예제 #3
0
        public virtual void test_currencyExposureBetweenFixingAndPayment()
        {
            double    eps           = 1.0e-14;
            LocalDate valuationDate = date(2014, 6, 30);
            LocalDate paymentDate   = date(2014, 7, 1);
            LocalDate fixingDate    = date(2014, 6, 27);
            FxResetNotionalExchange   resetNotionalUSD = FxResetNotionalExchange.of(CurrencyAmount.of(USD, NOTIONAL), paymentDate, FxIndexObservation.of(GBP_USD_WM, fixingDate, REF_DATA));
            FxResetNotionalExchange   resetNotionalGBP = FxResetNotionalExchange.of(CurrencyAmount.of(GBP, -NOTIONAL), paymentDate, FxIndexObservation.of(GBP_USD_WM, fixingDate, REF_DATA));
            LocalDateDoubleTimeSeries ts   = LocalDateDoubleTimeSeries.of(LocalDate.of(2014, 6, 27), 1.65);
            ImmutableRatesProvider    prov = ImmutableRatesProvider.builder(valuationDate).fxRateProvider(FX_MATRIX).discountCurve(GBP, DISCOUNT_CURVE_GBP).discountCurve(USD, DISCOUNT_CURVE_USD).timeSeries(FxIndices.GBP_USD_WM, ts).build();
            DiscountingFxResetNotionalExchangePricer test = new DiscountingFxResetNotionalExchangePricer();
            // USD
            MultiCurrencyAmount computedUSD = test.currencyExposure(resetNotionalUSD, prov);
            PointSensitivities  pointUSD    = test.presentValueSensitivity(resetNotionalUSD, prov).build();
            MultiCurrencyAmount expectedUSD = prov.currencyExposure(pointUSD.convertedTo(USD, prov)).plus(CurrencyAmount.of(resetNotionalUSD.Currency, test.presentValue(resetNotionalUSD, prov)));

            assertFalse(computedUSD.contains(USD));     // 0 USD
            assertEquals(computedUSD.getAmount(GBP).Amount, expectedUSD.getAmount(GBP).Amount, eps * NOTIONAL);
            // GBP
            MultiCurrencyAmount computedGBP = test.currencyExposure(resetNotionalGBP, prov);
            PointSensitivities  pointGBP    = test.presentValueSensitivity(resetNotionalGBP, prov).build();
            MultiCurrencyAmount expectedGBP = prov.currencyExposure(pointGBP.convertedTo(GBP, prov)).plus(CurrencyAmount.of(resetNotionalGBP.Currency, test.presentValue(resetNotionalGBP, prov)));

            assertFalse(computedGBP.contains(GBP));     // 0 GBP
            assertEquals(computedGBP.getAmount(USD).Amount, expectedGBP.getAmount(USD).Amount, eps * NOTIONAL);
            // FD approximation
            FxMatrix fxMatrixUp           = FxMatrix.of(GBP, USD, FX_RATE + EPS_FD);
            ImmutableRatesProvider provUp = ImmutableRatesProvider.builder(valuationDate).fxRateProvider(fxMatrixUp).discountCurve(GBP, DISCOUNT_CURVE_GBP).discountCurve(USD, DISCOUNT_CURVE_USD).timeSeries(FxIndices.GBP_USD_WM, ts).build();
            double expectedFdUSD          = -(test.presentValue(resetNotionalUSD, provUp) - test.presentValue(resetNotionalUSD, prov)) * FX_RATE * FX_RATE / EPS_FD;

            assertTrue(!computedUSD.contains(USD) && DoubleMath.fuzzyEquals(expectedFdUSD, 0d, eps));
            double expectedFdGBP = (test.presentValue(resetNotionalGBP, provUp) - test.presentValue(resetNotionalGBP, prov)) / EPS_FD;

            assertTrue(!computedGBP.contains(GBP) && DoubleMath.fuzzyEquals(expectedFdGBP, 0d, eps));
        }
예제 #4
0
 /// <summary>
 /// Creates an instance.
 /// </summary>
 /// <param name="mu"> The location parameter </param>
 /// <param name="sigma"> The scale parameter, not negative or zero </param>
 /// <param name="ksi"> The shape parameter </param>
 public GeneralizedExtremeValueDistribution(double mu, double sigma, double ksi)
 {
     ArgChecker.isTrue(sigma >= 0, "sigma must be >= 0");
     _mu        = mu;
     _sigma     = sigma;
     _ksi       = ksi;
     _ksiIsZero = DoubleMath.fuzzyEquals(ksi, 0d, 1e-13);
 }
 /// <summary>
 /// Creates an instance.
 /// </summary>
 /// <param name="mu"> The location parameter </param>
 /// <param name="sigma"> The scale parameter </param>
 /// <param name="ksi"> The shape parameter </param>
 /// <param name="engine"> A uniform random number generator, not null </param>
 public GeneralizedParetoDistribution(double mu, double sigma, double ksi, RandomEngine engine)
 {
     ArgChecker.isTrue(sigma > 0, "sigma must be > 0");
     ArgChecker.isTrue(!DoubleMath.fuzzyEquals(ksi, 0d, 1e-15), "ksi cannot be zero");
     ArgChecker.notNull(engine, "engine");
     _mu     = mu;
     _sigma  = sigma;
     _ksi    = ksi;
     _engine = engine;
 }
 //-------------------------------------------------------------------------
 /// <summary>
 /// Compares each element in the array to zero within a tolerance.
 /// <para>
 /// An empty array returns true;
 /// </para>
 /// <para>
 /// The input array is not mutated.
 ///
 /// </para>
 /// </summary>
 /// <param name="array">  the array to check </param>
 /// <param name="tolerance">  the tolerance to use </param>
 /// <returns> true if the array is effectively equal to zero </returns>
 public static bool fuzzyEqualsZero(double[] array, double tolerance)
 {
     for (int i = 0; i < array.Length; i++)
     {
         if (!DoubleMath.fuzzyEquals(array[i], 0, tolerance))
         {
             return(false);
         }
     }
     return(true);
 }
예제 #7
0
 //-------------------------------------------------------------------------
 /// <summary>
 /// Converts this amount to an equivalent amount the specified currency.
 /// <para>
 /// The result will be expressed in terms of the given currency, converting
 /// using the specified FX rate.
 /// </para>
 /// <para>
 /// For example, if this represents 'GBP 100' and this method is called with
 /// arguments {@code (USD, 1.6)} then the result will be 'USD 160'.
 ///
 /// </para>
 /// </summary>
 /// <param name="resultCurrency">  the currency of the result </param>
 /// <param name="fxRate">  the FX rate from this currency to the result currency </param>
 /// <returns> the converted instance, which should be expressed in the specified currency </returns>
 /// <exception cref="IllegalArgumentException"> if the FX is not 1 when no conversion is required </exception>
 public virtual Money convertedTo(Currency resultCurrency, decimal fxRate)
 {
     if (currency.Equals(resultCurrency))
     {
         if (DoubleMath.fuzzyEquals(fxRate.doubleValue(), 1d, 1e-8))
         {
             return(this);
         }
         throw new System.ArgumentException("FX rate must be 1 when no conversion required");
     }
     return(Money.of(resultCurrency, amount * fxRate));
 }
 /// <summary>
 /// Converts this amount to an equivalent amount the specified currency.
 /// <para>
 /// The result will be expressed in terms of the given currency, converting
 /// using the specified FX rate.
 /// </para>
 /// <para>
 /// For example, if this represents 'GBP 100' and this method is called with
 /// arguments {@code (USD, 1.6)} then the result will be 'USD 160'.
 ///
 /// </para>
 /// </summary>
 /// <param name="resultCurrency">  the currency of the result </param>
 /// <param name="fxRate">  the FX rate from this currency to the result currency </param>
 /// <returns> the converted instance, which should be expressed in the specified currency </returns>
 /// <exception cref="IllegalArgumentException"> if the FX is not 1 when no conversion is required </exception>
 public CurrencyAmount convertedTo(Currency resultCurrency, double fxRate)
 {
     if (currency.Equals(resultCurrency))
     {
         if (DoubleMath.fuzzyEquals(fxRate, 1d, 1e-8))
         {
             return(this);
         }
         throw new System.ArgumentException("FX rate must be 1 when no conversion required");
     }
     return(CurrencyAmount.of(resultCurrency, amount * fxRate));
 }
 /// <summary>
 /// Compares each element in the first array to the matching index in the second array within a tolerance.
 /// <para>
 /// If the arrays differ in length, false is returned.
 /// </para>
 /// <para>
 /// The input arrays are not mutated.
 ///
 /// </para>
 /// </summary>
 /// <param name="array1">  the first array to check </param>
 /// <param name="array2">  the second array to check </param>
 /// <param name="tolerance">  the tolerance to use </param>
 /// <returns> true if the arrays are effectively equal </returns>
 public static bool fuzzyEquals(double[] array1, double[] array2, double tolerance)
 {
     if (array1.Length != array2.Length)
     {
         return(false);
     }
     for (int i = 0; i < array1.Length; i++)
     {
         if (!DoubleMath.fuzzyEquals(array1[i], array2[i], tolerance))
         {
             return(false);
         }
     }
     return(true);
 }
예제 #10
0
        private static CurveNode curveFxSwapCurveNode(string conventionStr, string timeStr, string label, QuoteId quoteId, double spread, CurveNodeDate date, CurveNodeDateOrder order)
        {
            if (!DoubleMath.fuzzyEquals(spread, 0d, 1e-10d))
            {
                throw new System.ArgumentException("Additional spread must be zero for FX swaps");
            }
            Matcher matcher = SIMPLE_YMD_TIME_REGEX.matcher(timeStr.ToUpper(Locale.ENGLISH));

            if (!matcher.matches())
            {
                throw new System.ArgumentException(Messages.format("Invalid time format for FX swap: {}", timeStr));
            }
            Period           periodToEnd = Period.parse("P" + matcher.group(1));
            FxSwapConvention convention  = FxSwapConvention.of(conventionStr);
            FxSwapTemplate   template    = FxSwapTemplate.of(periodToEnd, convention);

            return(FxSwapCurveNode.builder().template(template).farForwardPointsId(quoteId).label(label).date(date).dateOrder(order).build());
        }
        public virtual double?[] getRoots(RealPolynomialFunction1D function)
        {
            ArgChecker.notNull(function, "function");
            double[] coefficients = function.Coefficients;
            if (coefficients.Length != 4)
            {
                throw new System.ArgumentException("Function is not a cubic");
            }
            ComplexNumber[] result = ROOT_FINDER.getRoots(function);
            IList <double>  reals  = new List <double>();

            foreach (ComplexNumber c in result)
            {
                if (DoubleMath.fuzzyEquals(c.Imaginary, 0d, 1e-16))
                {
                    reals.Add(c.Real);
                }
            }
            ArgChecker.isTrue(reals.Count > 0, "Could not find any real roots");
            return(reals.toArray(EMPTY_ARRAY));
        }
        /// <summary>
        /// {@inheritDoc} </summary>
        /// <exception cref="IllegalArgumentException"> If the function is not cubic </exception>
        public virtual ComplexNumber[] getRoots(RealPolynomialFunction1D function)
        {
            ArgChecker.notNull(function, "function");
            double[] coefficients = function.Coefficients;
            ArgChecker.isTrue(coefficients.Length == 4, "Function is not a cubic");
            double divisor  = coefficients[3];
            double a        = coefficients[2] / divisor;
            double b        = coefficients[1] / divisor;
            double c        = coefficients[0] / divisor;
            double aSq      = a * a;
            double q        = (aSq - 3 * b) / 9;
            double r        = (2 * a * aSq - 9 * a * b + 27 * c) / 54;
            double rSq      = r * r;
            double qCb      = q * q * q;
            double constant = a / 3;

            if (rSq < qCb)
            {
                double mult  = -2 * Math.Sqrt(q);
                double theta = Math.Acos(r / Math.Sqrt(qCb));
                return(new ComplexNumber[]
                {
                    new ComplexNumber(mult * Math.Cos(theta / 3) - constant, 0),
                    new ComplexNumber(mult * Math.Cos((theta + TWO_PI) / 3) - constant, 0),
                    new ComplexNumber(mult * Math.Cos((theta - TWO_PI) / 3) - constant, 0)
                });
            }
            double s         = -Math.Sign(r) * Math.cbrt(Math.Abs(r) + Math.Sqrt(rSq - qCb));
            double t         = DoubleMath.fuzzyEquals(s, 0d, 1e-16) ? 0 : q / s;
            double sum       = s + t;
            double real      = -0.5 * sum - constant;
            double imaginary = Math.Sqrt(3) * (s - t) / 2;

            return(new ComplexNumber[]
            {
                new ComplexNumber(sum - constant, 0),
                new ComplexNumber(real, imaginary),
                new ComplexNumber(real, -imaginary)
            });
        }
예제 #13
0
        public virtual void test_pv01_quote()
        {
            FixedCouponBondTradeCalculationFunction <FixedCouponBondTrade> function = FixedCouponBondTradeCalculationFunction.TRADE;
            ScenarioMarketData                    md                      = marketData();
            LegalEntityDiscountingProvider        provider                = LOOKUP.marketDataView(md.scenario(0)).discountingProvider();
            DiscountingFixedCouponBondTradePricer pricer                  = DiscountingFixedCouponBondTradePricer.DEFAULT;
            PointSensitivities                    pvPointSens             = pricer.presentValueSensitivity(RTRADE, provider);
            CurrencyParameterSensitivities        pvParamSens             = provider.parameterSensitivity(pvPointSens);
            CurrencyParameterSensitivities        expectedPv01CalBucketed = MQ_CALC.sensitivity(pvParamSens, provider).multipliedBy(1e-4);
            MultiCurrencyAmount                   expectedPv01Cal         = expectedPv01CalBucketed.total();

            ISet <Measure> measures = ImmutableSet.of(Measures.PV01_MARKET_QUOTE_SUM, Measures.PV01_MARKET_QUOTE_BUCKETED);
//JAVA TO C# CONVERTER WARNING: Java wildcard generics have no direct equivalent in .NET:
//ORIGINAL LINE: java.util.Map<com.opengamma.strata.calc.Measure, com.opengamma.strata.collect.result.Result<?>> computed = function.calculate(TRADE, measures, PARAMS, md, REF_DATA);
            IDictionary <Measure, Result <object> > computed = function.calculate(TRADE, measures, PARAMS, md, REF_DATA);
            MultiCurrencyScenarioArray sumComputed           = (MultiCurrencyScenarioArray)computed[Measures.PV01_MARKET_QUOTE_SUM].Value;
            ScenarioArray <CurrencyParameterSensitivities> bucketedComputed = (ScenarioArray <CurrencyParameterSensitivities>)computed[Measures.PV01_MARKET_QUOTE_BUCKETED].Value;

            assertEquals(sumComputed.ScenarioCount, 1);
            assertEquals(sumComputed.get(0).Currencies, ImmutableSet.of(GBP));
            assertTrue(DoubleMath.fuzzyEquals(sumComputed.get(0).getAmount(GBP).Amount, expectedPv01Cal.getAmount(GBP).Amount, 1.0e-10));
            assertEquals(bucketedComputed.ScenarioCount, 1);
            assertTrue(bucketedComputed.get(0).equalWithTolerance(expectedPv01CalBucketed, 1.0e-10));
        }
        private double getZOverChi(double rho, double z)
        {
            // Implementation comment: To avoid numerical instability (0/0) around ATM the first order approximation is used.
            if (DoubleMath.fuzzyEquals(z, 0.0, SMALL_Z))
            {
                return(1.0 - rho * z / 2.0);
            }

            double rhoStar = 1 - rho;

            if (DoubleMath.fuzzyEquals(rhoStar, 0.0, RHO_EPS))
            {
                if (z < 1.0)
                {
                    return(-z / Math.Log(1.0d - z));
                }
                else
                {
                    throw new System.ArgumentException("can't handle z>=1, rho=1");
                }
            }

            double rhoHat = 1 + rho;

            if (DoubleMath.fuzzyEquals(rhoHat, 0.0, RHO_EPS_NEGATIVE))
            {
                if (z > -1)
                {
                    return(z / Math.Log(1 + z));
                }
                else if (z < -1)
                {
                    if (rhoHat == 0)
                    {
                        return(0.0);
                    }
                    double chi = Math.Log(rhoHat) - Math.Log(-(1 + z) / rhoStar);
                    return(z / chi);
                }
                else
                {
                    return(0.0);
                }
            }

            double arg;

            if (z < LARGE_NEG_Z)
            {
                arg = (rho * rho - 1) / 2 / z;   //get rounding errors due to fine balanced cancellation for very large negative z
            }
            else if (z > LARGE_POS_Z)
            {
                arg = 2 * (z - rho);
            }
            else
            {
                arg = (Math.Sqrt(1 - 2 * rho * z + z * z) + z - rho);
                //Mathematically this cannot be less than zero, but you know what computers are like.
                if (arg <= 0.0)
                {
                    return(0.0);
                }
            }

            double chi = Math.Log(arg) - Math.Log(rhoStar);

            return(z / chi);
        }
        /// <summary>
        /// Computes the implied volatility in the SABR model and its derivatives.
        /// <para>
        /// The derivatives are stored in an array with:
        /// <ul>
        /// <li>[0] derivative with respect to the forward
        /// <li>[1] derivative with respect to the strike
        /// <li>[2] derivative with respect to the alpha
        /// <li>[3] derivative with respect to the beta
        /// <li>[4] derivative with respect to the rho
        /// <li>[5] derivative with respect to the nu
        /// </ul>
        ///
        /// </para>
        /// </summary>
        /// <param name="forward">  the forward value of the underlying </param>
        /// <param name="strike">  the strike value of the option </param>
        /// <param name="timeToExpiry">  the time to expiry of the option </param>
        /// <param name="alpha">  the SABR alpha value </param>
        /// <param name="beta">  the SABR beta value </param>
        /// <param name="rho">  the SABR rho value </param>
        /// <param name="nu">  the SABR nu value </param>
        /// <returns> the volatility and associated derivatives </returns>
        public ValueDerivatives volatilityAdjoint(double forward, double strike, double timeToExpiry, double alpha, double beta, double rho, double nu)
        {
            ArgChecker.isTrue(forward > 0.0, "forward must be greater than zero");
            ArgChecker.isTrue(strike >= 0.0, "strike must be greater than zero");
            ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry must be greater than zero");
            double cutoff = forward * CUTOFF_MONEYNESS;
            double k      = strike;

            if (k < cutoff)
            {
                log.info("Given strike of {} is less than cutoff at {}, therefore the strike is taken as {}", new object[] { k, cutoff, cutoff });
                k = cutoff;
            }
            double betaStar = 1 - beta;
            double rhoStar  = 1.0 - rho;

            if (alpha == 0.0)
            {
                double alphaBar;
                if (DoubleMath.fuzzyEquals(forward, k, ATM_EPS))
                {   //TODO should this is relative
                    alphaBar = (1 + (2 - 3 * rho * rho) * nu * nu / 24 * timeToExpiry) / Math.Pow(forward, betaStar);
                }
                else
                {
                    //for non-atm options the alpha sensitivity at alpha = 0 is infinite. Returning this will most likely break calibrations,
                    // so we return an arbitrary large number
                    alphaBar = 1e7;
                }
                return(ValueDerivatives.of(0d, DoubleArray.of(0, 0, alphaBar, 0, 0, 0)));
            }

            // Implementation note: Forward sweep.
            double sfK   = Math.Pow(forward * k, betaStar / 2);
            double lnrfK = Math.Log(forward / k);
            double z     = nu / alpha * sfK * lnrfK;
            double rzxz;
            double xz = 0;

            if (DoubleMath.fuzzyEquals(z, 0.0, SMALL_Z))
            {
                rzxz = 1.0 - 0.5 * z * rho;   //small z expansion to z^2 terms
            }
            else
            {
                if (DoubleMath.fuzzyEquals(rhoStar, 0.0, RHO_EPS))
                {
                    if (z < 1.0)
                    {
                        xz   = -Math.Log(1.0d - z);
                        rzxz = z / xz;
                    }
                    else
                    {
                        throw new System.ArgumentException("can't handle z>=1, rho=1");
                    }
                }
                else
                {
                    double arg;
                    if (z < LARGE_NEG_Z)
                    {
                        arg = (rho * rho - 1) / 2 / z;   //get rounding errors due to fine balanced cancellation for very large negative z
                    }
                    else if (z > LARGE_POS_Z)
                    {
                        arg = 2 * (z - rho);
                    }
                    else
                    {
                        arg = (Math.Sqrt(1 - 2 * rho * z + z * z) + z - rho);
                    }
                    if (arg <= 0.0)
                    {     //Mathematically this cannot be less than zero, but you know what computers are like.
                        rzxz = 0.0;
                    }
                    else
                    {
                        xz   = Math.Log(arg / (1 - rho));
                        rzxz = z / xz;
                    }
                }
            }
            double sf1        = sfK * (1 + betaStar * betaStar / 24 * (lnrfK * lnrfK) + Math.Pow(betaStar, 4) / 1920 * Math.Pow(lnrfK, 4));
            double sf2        = (1 + (Math.Pow(betaStar * alpha / sfK, 2) / 24 + (rho * beta * nu * alpha) / (4 * sfK) + (2 - 3 * rho * rho) * nu * nu / 24) * timeToExpiry);
            double volatility = Math.Max(MIN_VOL, alpha / sf1 * rzxz * sf2);

            // Implementation note: Backward sweep.
            double vBar    = 1;
            double sf2Bar  = alpha / sf1 * rzxz * vBar;
            double sf1Bar  = -alpha / (sf1 * sf1) * rzxz * sf2 * vBar;
            double rzxzBar = alpha / sf1 * sf2 * vBar;
            double zBar;
            double xzBar = 0.0;

            if (DoubleMath.fuzzyEquals(z, 0.0, SMALL_Z))
            {
                zBar = -rho / 2 * rzxzBar;
            }
            else
            {
                if (DoubleMath.fuzzyEquals(rhoStar, 0.0, RHO_EPS))
                {
                    if (z < 1.0)
                    {
                        xzBar = -z / (xz * xz) * rzxzBar;
                        zBar  = 1.0d / xz * rzxzBar + 1.0d / (1.0d - z) * xzBar;
                    }
                    else
                    {
                        throw new System.ArgumentException("can't handle z>=1, rho=1");
                    }
                }
                else
                {
                    if (z < LARGE_NEG_Z)
                    {
                        zBar = 1 / xz * rzxzBar + xzBar / (xz * xz) * rzxzBar;
                    }
                    else if (z > LARGE_POS_Z)
                    {
                        zBar = 1 / xz * rzxzBar - xzBar / (xz * xz) * rzxzBar;
                    }
                    else
                    {
                        xzBar = -z / (xz * xz) * rzxzBar;
                        zBar  = 1 / xz * rzxzBar + 1 / ((Math.Sqrt(1 - 2 * rho * z + z * z) + z - rho)) * (0.5 * Math.Pow(1 - 2 * rho * z + z * z, -0.5) * (-2 * rho + 2 * z) + 1) * xzBar;
                    }
                }
            }

            double lnrfKBar   = sfK * (betaStar * betaStar / 12 * lnrfK + Math.Pow(betaStar, 4) / 1920 * 4 * Math.Pow(lnrfK, 3)) * sf1Bar + nu / alpha * sfK * zBar;
            double sfKBar     = nu / alpha * lnrfK * zBar + sf1 / sfK * sf1Bar - (Math.Pow(betaStar * alpha, 2) / Math.Pow(sfK, 3) / 12 + (rho * beta * nu * alpha) / 4 / (sfK * sfK)) * timeToExpiry * sf2Bar;
            double strikeBar  = -1 / k * lnrfKBar + betaStar * sfK / (2 * k) * sfKBar;
            double forwardBar = 1 / forward * lnrfKBar + betaStar * sfK / (2 * forward) * sfKBar;
            double nuBar      = 1 / alpha * sfK * lnrfK * zBar + ((rho * beta * alpha) / (4 * sfK) + (2 - 3 * rho * rho) * nu / 12) * timeToExpiry * sf2Bar;

            double rhoBar;

            if (Math.Abs(forward - k) < ATM_EPS)
            {
                rhoBar = -z / 2 * rzxzBar;
            }
            else
            {
                if (DoubleMath.fuzzyEquals(rhoStar, 0.0, RHO_EPS))
                {
                    if (z >= 1)
                    {
                        if (rhoStar == 0.0)
                        {
                            rhoBar = double.NegativeInfinity;     //the derivative at rho = 1 is infinite  - this sets it to some arbitrary large number
                        }
                        else
                        {
                            rhoBar = xzBar * (1.0 / rhoStar + (0.5 - z) / (z - 1.0) / (z - 1.0));
                        }
                    }
                    else
                    {
                        rhoBar = (0.5 * Math.Pow(z / (1 - z), 2) + 0.25 * (z - 4.0) * Math.Pow(z / (1.0 - z), 3) / (1.0 - z) * rhoStar) * xzBar;
                    }
                }
                else
                {
                    rhoBar = (1 / (Math.Sqrt(1 - 2 * rho * z + z * z) + z - rho) * (-Math.Pow(1 - 2 * rho * z + z * z, -0.5) * z - 1) + 1 / rhoStar) * xzBar;
                }
            }
            rhoBar += ((beta * nu * alpha) / (4 * sfK) - rho * nu * nu / 4) * timeToExpiry * sf2Bar;

            double alphaBar = -nu / (alpha * alpha) * sfK * lnrfK * zBar + ((betaStar * alpha / sfK) * (betaStar / sfK) / 12 + (rho * beta * nu) / (4 * sfK)) * timeToExpiry * sf2Bar + 1 / sf1 * rzxz * sf2 * vBar;
            double betaBar  = -0.5 * Math.Log(forward * k) * sfK * sfKBar - sfK * (betaStar / 12 * (lnrfK * lnrfK) + Math.Pow(betaStar, 3) / 480 * Math.Pow(lnrfK, 4)) * sf1Bar + (-betaStar * alpha * alpha / sfK / sfK / 12 + rho * nu * alpha / 4 / sfK) * timeToExpiry * sf2Bar;

            return(ValueDerivatives.of(volatility, DoubleArray.of(forwardBar, strikeBar, alphaBar, betaBar, rhoBar, nuBar)));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Gets the amount as a string.
        /// <para>
        /// The format is the currency code, followed by a space, followed by the
        /// amount: '${currency} ${amount}'.
        ///
        /// </para>
        /// </summary>
        /// <returns> the currency amount </returns>
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Override @ToString public String toString()
        public override string ToString()
        {
            return(currency + " " + (DoubleMath.isMathematicalInteger(amount) ? Convert.ToString((long)amount) : Convert.ToString(amount)));
        }
        public double volatility(double forward, double strike, double timeToExpiry, double alpha, double beta, double rho, double nu)
        {
            ArgChecker.isTrue(forward > 0.0, "forward must be greater than zero");
            ArgChecker.isTrue(strike >= 0.0, "strike must be greater than zero");
            ArgChecker.isTrue(timeToExpiry >= 0.0, "timeToExpiry must be greater than zero");

            if (alpha == 0.0)
            {
                return(0.0);
            }
            double cutoff = forward * CUTOFF_MONEYNESS;
            double k;

            if (strike < cutoff)
            {
                log.info("Given strike of {} is less than cutoff at {}, therefore the strike is taken as {}", new object[] { strike, cutoff, cutoff });
                k = cutoff;
            }
            else
            {
                k = strike;
            }
            double vol, z, zOverChi;
            double beta1 = 1 - beta;

            if (DoubleMath.fuzzyEquals(forward, k, ATM_EPS))
            {
                double f1 = Math.Pow(forward, beta1);
                vol = alpha * (1 + timeToExpiry * (beta1 * beta1 * alpha * alpha / 24 / f1 / f1 + rho * alpha * beta * nu / 4 / f1 + nu * nu * (2 - 3 * rho * rho) / 24)) / f1;
            }
            else
            {
                if (DoubleMath.fuzzyEquals(beta, 0, BETA_EPS))
                {
                    double ln = Math.Log(forward / k);
                    z        = nu * Math.Sqrt(forward * k) * ln / alpha;
                    zOverChi = getZOverChi(rho, z);
                    vol      = alpha * ln * zOverChi * (1 + timeToExpiry * (alpha * alpha / forward / k + nu * nu * (2 - 3 * rho * rho)) / 24) / (forward - k);
                }
                else if (DoubleMath.fuzzyEquals(beta, 1, BETA_EPS))
                {
                    double ln = Math.Log(forward / k);
                    z        = nu * ln / alpha;
                    zOverChi = getZOverChi(rho, z);
                    vol      = alpha * zOverChi * (1 + timeToExpiry * (rho * alpha * nu / 4 + nu * nu * (2 - 3 * rho * rho) / 24));
                }
                else
                {
                    double ln       = Math.Log(forward / k);
                    double f1       = Math.Pow(forward * k, beta1);
                    double f1Sqrt   = Math.Sqrt(f1);
                    double lnBetaSq = Math.Pow(beta1 * ln, 2);
                    z        = nu * f1Sqrt * ln / alpha;
                    zOverChi = getZOverChi(rho, z);
                    double first  = alpha / (f1Sqrt * (1 + lnBetaSq / 24 + lnBetaSq * lnBetaSq / 1920));
                    double second = zOverChi;
                    double third  = 1 + timeToExpiry * (beta1 * beta1 * alpha * alpha / 24 / f1 + rho * nu * beta * alpha / 4 / f1Sqrt + nu * nu * (2 - 3 * rho * rho) / 24);
                    vol = first * second * third;
                }
            }
            //There is nothing to prevent the nu * nu * (2 - 3 * rho * rho) / 24 to be large negative, and hence the volatility negative
            return(Math.Max(MIN_VOL, vol));
        }
        //-------------------------------------------------------------------------
        public override double?apply(double?x)
        {
            ArgChecker.inRangeInclusive(x, 0d, 1d, "x");
            double pp, p, t, h, w, lnA, lnB, u, a1 = _a - 1;
            double b1 = _b - 1;

            if (_a >= 1 && _b >= 1)
            {
                pp = x < 0.5 ? x.Value : 1 - x;
                t  = Math.Sqrt(-2 * Math.Log(pp));
                p  = (2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t;
                if (p < 0.5)
                {
                    p *= -1;
                }
                a1 = (Math.Sqrt(p) - 3.0) / 6.0;
                double tempA = 1.0 / (2 * _a - 1);
                double tempB = 1.0 / (2 * _b - 1);
                h = 2.0 / (tempA + tempB);
                w = p * Math.Sqrt(a1 + h) / h - (tempB - tempA) * (a1 + 5.0 / 6 - 2.0 / (3 * h));
                p = _a / (_a + _b + Math.Exp(2 * w));
            }
            else
            {
                lnA = Math.Log(_a / (_a + _b));
                lnB = Math.Log(_b / (_a + _b));
                t   = Math.Exp(_a * lnA) / _a;
                u   = Math.Exp(_b * lnB) / _b;
                w   = t + u;
                if (x.Value < t / w)
                {
                    p = Math.Pow(_a * w * x, 1.0 / _a);
                }
                else
                {
                    p = 1 - Math.Pow(_b * w * (1 - x), 1.0 / _b);
                }
            }
            double afac = -_lnGamma.apply(_a) - _lnGamma.apply(_b) + _lnGamma.apply(_a + _b);
            double error;

            for (int j = 0; j < 10; j++)
            {
                if (DoubleMath.fuzzyEquals(p, 0d, 1e-16) || DoubleMath.fuzzyEquals(p, (double)1, 1e-16))
                {
                    throw new MathException("a or b too small for accurate evaluation");
                }
                error = _beta.apply(p) - x;
                t     = Math.Exp(a1 * Math.Log(p) + b1 * Math.Log(1 - p) + afac);
                u     = error / t;
                t     = u / (1 - 0.5 * Math.Min(1, u * (a1 / p - b1 / (1 - p))));
                p    -= t;
                if (p <= 0)
                {
                    p = 0.5 * (p + t);
                }
                if (p >= 1)
                {
                    p = 0.5 * (p + t + 1);
                }
                if (Math.Abs(t) < EPS * p && j > 0)
                {
                    break;
                }
            }
            return(p);
        }
        /// <summary>
        /// Computes the first and second order derivatives of the Black implied volatility in the SABR model.
        /// <para>
        /// The first derivative values will be stored in the input array {@code volatilityD}
        /// The array contains, [0] Derivative w.r.t the forward, [1] the derivative w.r.t the strike, [2] the derivative w.r.t. to alpha,
        /// [3] the derivative w.r.t. to beta, [4] the derivative w.r.t. to rho, and [5] the derivative w.r.t. to nu.
        /// Thus the length of the array should be 6.
        /// </para>
        /// <para>
        /// The second derivative values will be stored in the input array {@code volatilityD2}.
        /// Only the second order derivative with respect to the forward and strike are implemented.
        /// The array contains [0][0] forward-forward; [0][1] forward-strike; [1][1] strike-strike.
        /// Thus the size should be 2 x 2.
        /// </para>
        /// <para>
        /// Around ATM, a first order expansion is used to due to some 0/0-type indetermination.
        /// The second order derivative produced is poor around ATM.
        ///
        /// </para>
        /// </summary>
        /// <param name="forward">  the forward value of the underlying </param>
        /// <param name="strike">  the strike value of the option </param>
        /// <param name="timeToExpiry">  the time to expiry of the option </param>
        /// <param name="data">  the SABR data. </param>
        /// <param name="volatilityD">  the array used to return the first order derivative </param>
        /// <param name="volatilityD2">  the array of array used to return the second order derivative </param>
        /// <returns> the Black implied volatility </returns>
        public override double volatilityAdjoint2(double forward, double strike, double timeToExpiry, SabrFormulaData data, double[] volatilityD, double[][] volatilityD2)
        {
            double k     = Math.Max(strike, 0.000001);
            double alpha = data.Alpha;
            double beta  = data.Beta;
            double rho   = data.Rho;
            double nu    = data.Nu;
            // Forward
            double h0 = (1 - beta) / 2;
            double h1 = forward * k;
            double h1h0 = Math.Pow(h1, h0);
            double h12 = h1h0 * h1h0;
            double h2 = Math.Log(forward / k);
            double h22 = h2 * h2;
            double h23 = h22 * h2;
            double h24 = h23 * h2;
            double f1 = h1h0 * (1 + h0 * h0 / 6.0 * (h22 + h0 * h0 / 20.0 * h24));
            double f2 = nu / alpha * h1h0 * h2;
            double f3 = h0 * h0 / 6.0 * alpha * alpha / h12 + rho * beta * nu * alpha / 4.0 / h1h0 + (2 - 3 * rho * rho) / 24.0 * nu * nu;
            double sqrtf2 = Math.Sqrt(1 - 2 * rho * f2 + f2 * f2);
            double f2x = 0.0;
            double x = 0.0, xp = 0, xpp = 0;

            if (DoubleMath.fuzzyEquals(f2, 0.0, SMALL_Z))
            {
                f2x = 1.0 - 0.5 * f2 * rho;   //small f2 expansion to f2^2 terms
            }
            else
            {
                if (DoubleMath.fuzzyEquals(rho, 1.0, RHO_EPS))
                {
                    x = f2 < 1.0 ? -Math.Log(1.0 - f2) - 0.5 * Math.Pow(f2 / (f2 - 1.0), 2) * (1.0 - rho) : Math.Log(2.0 * f2 - 2.0) - Math.Log(1.0 - rho);
                }
                else
                {
                    x = Math.Log((sqrtf2 + f2 - rho) / (1 - rho));
                }
                xp  = 1.0 / sqrtf2;
                xpp = (rho - f2) / Math.Pow(sqrtf2, 3.0);
                f2x = f2 / x;
            }
            double sigma = Math.Max(MIN_VOL, alpha / f1 * f2x * (1 + f3 * timeToExpiry));
            // First level
            double h0Dbeta  = -0.5;
            double sigmaDf1 = -sigma / f1;
            double sigmaDf2 = 0;

            if (DoubleMath.fuzzyEquals(f2, 0.0, SMALL_Z))
            {
                sigmaDf2 = alpha / f1 * (1 + f3 * timeToExpiry) * -0.5 * rho;
            }
            else
            {
                sigmaDf2 = alpha / f1 * (1 + f3 * timeToExpiry) * (1.0 / x - f2 * xp / (x * x));
            }
            double sigmaDf3 = alpha / f1 * f2x * timeToExpiry;
            double sigmaDf4 = f2x / f1 * (1 + f3 * timeToExpiry);
            double sigmaDx  = -alpha / f1 * f2 / (x * x) * (1 + f3 * timeToExpiry);

//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[][] sigmaD2ff = new double[3][3];
            double[][] sigmaD2ff = RectangularArrays.ReturnRectangularDoubleArray(3, 3);
            sigmaD2ff[0][0] = -sigmaDf1 / f1 + sigma / (f1 * f1);     //OK
            sigmaD2ff[0][1] = -sigmaDf2 / f1;
            sigmaD2ff[0][2] = -sigmaDf3 / f1;
            if (DoubleMath.fuzzyEquals(f2, 0.0, SMALL_Z))
            {
                sigmaD2ff[1][2] = alpha / f1 * -0.5 * rho * timeToExpiry;
            }
            else
            {
                sigmaD2ff[1][1] = alpha / f1 * (1 + f3 * timeToExpiry) * (-2 * xp / (x * x) - f2 * xpp / (x * x) + 2 * f2 * xp * xp / (x * x * x));
                sigmaD2ff[1][2] = alpha / f1 * timeToExpiry * (1.0 / x - f2 * xp / (x * x));
            }
            sigmaD2ff[2][2] = 0.0;
            //      double sigma = alpha / f1 * f2x * (1 + f3 * theta);
            // Second level
            double[] f1Dh = new double[3];
            double[] f2Dh = new double[3];
            double[] f3Dh = new double[3];
            f1Dh[0] = h1h0 * (h0 * (h22 / 3.0 + h0 * h0 / 40.0 * h24)) + Math.Log(h1) * f1;
            f1Dh[1] = h0 * f1 / h1;
            f1Dh[2] = h1h0 * (h0 * h0 / 6.0 * (2.0 * h2 + h0 * h0 / 5.0 * h23));
            f2Dh[0] = Math.Log(h1) * f2;
            f2Dh[1] = h0 * f2 / h1;
            f2Dh[2] = nu / alpha * h1h0;
            f3Dh[0] = h0 / 3.0 * alpha * alpha / h12 - 2 * h0 * h0 / 6.0 * alpha * alpha / h12 * Math.Log(h1) - rho * beta * nu * alpha / 4.0 / h1h0 * Math.Log(h1);
            f3Dh[1] = -2 * h0 * h0 / 6.0 * alpha * alpha / h12 * h0 / h1 - rho * beta * nu * alpha / 4.0 / h1h0 * h0 / h1;
            f3Dh[2] = 0.0;
            double[] f1Dp = new double[4];     // Derivative to sabr parameters
            double[] f2Dp = new double[4];
            double[] f3Dp = new double[4];
            double[] f4Dp = new double[4];
            f1Dp[0] = 0.0;
            f1Dp[1] = f1Dh[0] * h0Dbeta;
            f1Dp[2] = 0.0;
            f1Dp[3] = 0.0;
            f2Dp[0] = -f2 / alpha;
            f2Dp[1] = f2Dh[0] * h0Dbeta;
            f2Dp[2] = 0.0;
            f2Dp[3] = h1h0 * h2 / alpha;
            f3Dp[0] = h0 * h0 / 3.0 * alpha / h12 + rho * beta * nu / 4.0 / h1h0;
            f3Dp[1] = rho * nu * alpha / 4.0 / h1h0 + f3Dh[0] * h0Dbeta;
            f3Dp[2] = beta * nu * alpha / 4.0 / h1h0 - rho / 4.0 * nu * nu;
            f3Dp[3] = rho * beta * alpha / 4.0 / h1h0 + (2 - 3 * rho * rho) / 12.0 * nu;
            f4Dp[0] = 1.0;
            f4Dp[1] = 0.0;
            f4Dp[2] = 0.0;
            f4Dp[3] = 0.0;
            double sigmaDh1 = sigmaDf1 * f1Dh[1] + sigmaDf2 * f2Dh[1] + sigmaDf3 * f3Dh[1];
            double sigmaDh2 = sigmaDf1 * f1Dh[2] + sigmaDf2 * f2Dh[2] + sigmaDf3 * f3Dh[2];

//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[][] f1D2hh = new double[2][2]; // No h0
            double[][] f1D2hh = RectangularArrays.ReturnRectangularDoubleArray(2, 2);     // No h0
//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[][] f2D2hh = new double[2][2];
            double[][] f2D2hh = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
//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[][] f3D2hh = new double[2][2];
            double[][] f3D2hh = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            f1D2hh[0][0] = h0 * (h0 - 1) * f1 / (h1 * h1);
            f1D2hh[0][1] = h0 * h1h0 / h1 * h0 * h0 / 6.0 * (2.0 * h2 + 4.0 * h0 * h0 / 20.0 * h23);
            f1D2hh[1][1] = h1h0 * (h0 * h0 / 6.0 * (2.0 + 12.0 * h0 * h0 / 20.0 * h2));
            f2D2hh[0][0] = h0 * (h0 - 1) * f2 / (h1 * h1);
            f2D2hh[0][1] = nu / alpha * h0 * h1h0 / h1;
            f2D2hh[1][1] = 0.0;
            f3D2hh[0][0] = 2 * h0 * (2 * h0 + 1) * h0 * h0 / 6.0 * alpha * alpha / (h12 * h1 * h1) + h0 * (h0 + 1) * rho * beta * nu * alpha / 4.0 / (h1h0 * h1 * h1);
            f3D2hh[0][1] = 0.0;
            f3D2hh[1][1] = 0.0;
//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[][] sigmaD2hh = new double[2][2]; // No h0
            double[][] sigmaD2hh = RectangularArrays.ReturnRectangularDoubleArray(2, 2);     // No h0
            for (int loopx = 0; loopx < 2; loopx++)
            {
                for (int loopy = loopx; loopy < 2; loopy++)
                {
                    sigmaD2hh[loopx][loopy] = (sigmaD2ff[0][0] * f1Dh[loopy + 1] + sigmaD2ff[0][1] * f2Dh[loopy + 1] + sigmaD2ff[0][2] * f3Dh[loopy + 1]) * f1Dh[loopx + 1] + sigmaDf1 * f1D2hh[loopx][loopy] + (sigmaD2ff[0][1] * f1Dh[loopy + 1] + sigmaD2ff[1][1] * f2Dh[loopy + 1] + sigmaD2ff[1][2] * f3Dh[loopy + 1]) * f2Dh[loopx + 1] + sigmaDf2 * f2D2hh[loopx][loopy] + (sigmaD2ff[0][2] * f1Dh[loopy + 1] + sigmaD2ff[1][2] * f2Dh[loopy + 1] + sigmaD2ff[2][2] * f3Dh[loopy + 1]) * f3Dh[loopx + 1] + sigmaDf3 * f3D2hh[loopx][loopy];
                }
            }
            // Third level
            double h1Df   = k;
            double h1Dk   = forward;
            double h1D2ff = 0.0;
            double h1D2kf = 1.0;
            double h1D2kk = 0.0;
            double h2Df   = 1.0 / forward;
            double h2Dk   = -1.0 / k;
            double h2D2ff = -1 / (forward * forward);
            double h2D2fk = 0.0;
            double h2D2kk = 1.0 / (k * k);

            volatilityD[0] = sigmaDh1 * h1Df + sigmaDh2 * h2Df;
            volatilityD[1] = sigmaDh1 * h1Dk + sigmaDh2 * h2Dk;
            volatilityD[2] = sigmaDf1 * f1Dp[0] + sigmaDf2 * f2Dp[0] + sigmaDf3 * f3Dp[0] + sigmaDf4 * f4Dp[0];
            volatilityD[3] = sigmaDf1 * f1Dp[1] + sigmaDf2 * f2Dp[1] + sigmaDf3 * f3Dp[1] + sigmaDf4 * f4Dp[1];
            if (DoubleMath.fuzzyEquals(f2, 0.0, SMALL_Z))
            {
                volatilityD[4] = -0.5 * f2 + sigmaDf3 * f3Dp[2];
            }
            else
            {
                double xDr;
                if (DoubleMath.fuzzyEquals(rho, 1.0, RHO_EPS))
                {
                    xDr = f2 > 1.0 ? 1.0 / (1.0 - rho) + (0.5 - f2) / (f2 - 1.0) / (f2 - 1.0) : 0.5 * Math.Pow(f2 / (1.0 - f2), 2.0) + 0.25 * (f2 - 4.0) * Math.Pow(f2 / (f2 - 1.0), 3) / (f2 - 1.0) * (1.0 - rho);
                    if (Doubles.isFinite(xDr))
                    {
                        volatilityD[4] = sigmaDf1 * f1Dp[2] + sigmaDx * xDr + sigmaDf3 * f3Dp[2] + sigmaDf4 * f4Dp[2];
                    }
                    else
                    {
                        volatilityD[4] = double.NegativeInfinity;
                    }
                }
                else
                {
                    xDr            = (-f2 / sqrtf2 - 1 + (sqrtf2 + f2 - rho) / (1 - rho)) / (sqrtf2 + f2 - rho);
                    volatilityD[4] = sigmaDf1 * f1Dp[2] + sigmaDx * xDr + sigmaDf3 * f3Dp[2] + sigmaDf4 * f4Dp[2];
                }
            }
            volatilityD[5]     = sigmaDf1 * f1Dp[3] + sigmaDf2 * f2Dp[3] + sigmaDf3 * f3Dp[3] + sigmaDf4 * f4Dp[3];
            volatilityD2[0][0] = (sigmaD2hh[0][0] * h1Df + sigmaD2hh[0][1] * h2Df) * h1Df + sigmaDh1 * h1D2ff + (sigmaD2hh[0][1] * h1Df + sigmaD2hh[1][1] * h2Df) * h2Df + sigmaDh2 * h2D2ff;
            volatilityD2[0][1] = (sigmaD2hh[0][0] * h1Dk + sigmaD2hh[0][1] * h2Dk) * h1Df + sigmaDh1 * h1D2kf + (sigmaD2hh[0][1] * h1Dk + sigmaD2hh[1][1] * h2Dk) * h2Df + sigmaDh2 * h2D2fk;
            volatilityD2[1][0] = volatilityD2[0][1];
            volatilityD2[1][1] = (sigmaD2hh[0][0] * h1Dk + sigmaD2hh[0][1] * h2Dk) * h1Dk + sigmaDh1 * h1D2kk + (sigmaD2hh[0][1] * h1Dk + sigmaD2hh[1][1] * h2Dk) * h2Dk + sigmaDh2 * h2D2kk;
            return(sigma);
        }