/// <summary>
        /// Test the payment delay convexity adjustment factor.
        /// </summary>
        public virtual void paymentDelayConvexityFactor()
        {
            double startExpiryTime   = 1.00;
            double endExpiryTime     = 3.00;
            double startFixingPeriod = 3.05;
            double endFixingPeriod   = 3.55;
            double paymentTime       = 3.45;
            double hwMeanReversion   = 0.011;
            // Constant volatility
            double hwEta = 0.02;
            HullWhiteOneFactorPiecewiseConstantParameters parameters = HullWhiteOneFactorPiecewiseConstantParameters.of(hwMeanReversion, DoubleArray.of(hwEta), DoubleArray.of());
            double factor1        = (Math.Exp(-hwMeanReversion * endFixingPeriod) - Math.Exp(-hwMeanReversion * paymentTime)) * (Math.Exp(-hwMeanReversion * endFixingPeriod) - Math.Exp(-hwMeanReversion * startFixingPeriod));
            double num            = 2 * Math.Pow(hwMeanReversion, 3);
            double factor2        = hwEta * hwEta * (Math.Exp(2 * hwMeanReversion * endExpiryTime) - Math.Exp(2 * hwMeanReversion * startExpiryTime));
            double factorExpected = Math.Exp(factor1 * factor2 / num);
            double factorComputed = MODEL.paymentDelayConvexityFactor(parameters, startExpiryTime, endExpiryTime, startFixingPeriod, endFixingPeriod, paymentTime);

            assertEquals(factorExpected, factorComputed, TOLERANCE_RATE);
            // Piecewise constant constant volatility
            double[] hwEtaP = new double[] { 0.02, 0.021, 0.022, 0.023 };
            double[] hwTime = new double[] { 0.5, 1.0, 2.0 };
            HullWhiteOneFactorPiecewiseConstantParameters parametersP = HullWhiteOneFactorPiecewiseConstantParameters.of(hwMeanReversion, DoubleArray.copyOf(hwEtaP), DoubleArray.copyOf(hwTime));
            double factorP2 = hwEtaP[2] * hwEtaP[2] * (Math.Exp(2 * hwMeanReversion * hwTime[2]) - Math.Exp(2 * hwMeanReversion * startExpiryTime));

            factorP2 += hwEtaP[3] * hwEtaP[3] * (Math.Exp(2 * hwMeanReversion * endExpiryTime) - Math.Exp(2 * hwMeanReversion * hwTime[2]));
            double factorPExpected = Math.Exp(factor1 * factorP2 / num);
            double factorPComputed = MODEL.paymentDelayConvexityFactor(parametersP, startExpiryTime, endExpiryTime, startFixingPeriod, endFixingPeriod, paymentTime);

            assertEquals(factorPExpected, factorPComputed, TOLERANCE_RATE);
        }
        /// <summary>
        /// Test the adjoint algorithmic differentiation version of alpha.
        /// </summary>
        public virtual void alphaDSigma()
        {
            double           expiry1      = 0.25;
            double           expiry2      = 2.25;
            double           numeraire    = 10.0;
            double           maturity     = 9.0;
            int              nbVolatility = VOLATILITY.size();
            ValueDerivatives alphaDeriv   = MODEL.alphaAdjoint(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity);
            double           alpha        = alphaDeriv.Value;

            double[] alphaDerivatives = alphaDeriv.Derivatives.toArray();
            double   alpha2           = MODEL.alpha(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity);

            assertEquals(alpha2, alpha, 1.0E-10);
            double shiftVol = 1.0E-6;

            double[] volatilityBumped = new double[nbVolatility];
            Array.Copy(VOLATILITY.toArray(), 0, volatilityBumped, 0, nbVolatility);
            double[] alphaBumpedPlus  = new double[nbVolatility];
            double[] alphaBumpedMinus = new double[nbVolatility];
            HullWhiteOneFactorPiecewiseConstantParameters parametersBumped;

            for (int loopvol = 0; loopvol < nbVolatility; loopvol++)
            {
                volatilityBumped[loopvol] += shiftVol;
                parametersBumped           = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volatilityBumped), VOLATILITY_TIME);
                alphaBumpedPlus[loopvol]   = MODEL.alpha(parametersBumped, expiry1, expiry2, numeraire, maturity);
                volatilityBumped[loopvol] -= 2 * shiftVol;
                parametersBumped           = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volatilityBumped), VOLATILITY_TIME);
                alphaBumpedMinus[loopvol]  = MODEL.alpha(parametersBumped, expiry1, expiry2, numeraire, maturity);
                assertEquals((alphaBumpedPlus[loopvol] - alphaBumpedMinus[loopvol]) / (2 * shiftVol), alphaDerivatives[loopvol], 1.0E-9);
                volatilityBumped[loopvol] = VOLATILITY.get(loopvol);
            }
        }
        /// <summary>
        /// Calculates the future convexity factor and its derivatives with respect to the model volatilities.
        /// <para>
        /// The factor is called gamma in the reference:
        /// Henrard, M. "The Irony in the derivatives discounting Part II: the crisis", Wilmott Journal, 2010, 2, 301-316
        ///
        /// </para>
        /// </summary>
        /// <param name="data">  the Hull-White model parameters </param>
        /// <param name="t0">  the expiry time </param>
        /// <param name="t1">  the first reference time </param>
        /// <param name="t2">  the second reference time </param>
        /// <returns> the factor and drivatives </returns>
        public ValueDerivatives futuresConvexityFactorAdjoint(HullWhiteOneFactorPiecewiseConstantParameters data, double t0, double t1, double t2)
        {
            double factor1   = Math.Exp(-data.MeanReversion * t1) - Math.Exp(-data.MeanReversion * t2);
            double numerator = 2 * data.MeanReversion * data.MeanReversion * data.MeanReversion;
            int    indexT0   = 1; // Period in which the time t0 is; volatilityTime[i-1] <= t0 < volatilityTime[i];

            while (t0 > data.VolatilityTime.get(indexT0))
            {
                indexT0++;
            }
            double[] s = new double[indexT0 + 1];
            Array.Copy(data.VolatilityTime.toArray(), 0, s, 0, indexT0);
            s[indexT0] = t0;
            double factor2 = 0.0;

            double[] factorExp = new double[indexT0];
            for (int loopperiod = 0; loopperiod < indexT0; loopperiod++)
            {
                factorExp[loopperiod] = (Math.Exp(data.MeanReversion * s[loopperiod + 1]) - Math.Exp(data.MeanReversion * s[loopperiod])) * (2 - Math.Exp(-data.MeanReversion * (t2 - s[loopperiod + 1])) - Math.Exp(-data.MeanReversion * (t2 - s[loopperiod])));
                factor2 += data.Volatility.get(loopperiod) * data.Volatility.get(loopperiod) * factorExp[loopperiod];
            }
            double factor = Math.Exp(factor1 / numerator * factor2);
            // Backward sweep
            double factorBar  = 1.0;
            double factor2Bar = factor1 / numerator * factor * factorBar;

            double[] derivatives = new double[data.Volatility.size()];
            for (int loopperiod = 0; loopperiod < indexT0; loopperiod++)
            {
                derivatives[loopperiod] = 2 * data.Volatility.get(loopperiod) * factorExp[loopperiod] * factor2Bar;
            }
            return(ValueDerivatives.of(factor, DoubleArray.ofUnsafe(derivatives)));
        }
        /// <summary>
        /// Test the future convexity adjustment factor v a hard-coded value.
        /// </summary>
        public virtual void futureConvexityFactor()
        {
            LocalDate SPOT_DATE         = LocalDate.of(2012, 9, 19);
            LocalDate LAST_TRADING_DATE = EURIBOR3M.calculateFixingFromEffective(SPOT_DATE, REF_DATA);
            LocalDate REFERENCE_DATE    = LocalDate.of(2010, 8, 18);
            double    tradeLastTime     = DayCounts.ACT_ACT_ISDA.relativeYearFraction(REFERENCE_DATE, LAST_TRADING_DATE);
            double    fixStartTime      = DayCounts.ACT_ACT_ISDA.relativeYearFraction(REFERENCE_DATE, SPOT_DATE);
            double    fixEndTime        = DayCounts.ACT_ACT_ISDA.relativeYearFraction(REFERENCE_DATE, EURIBOR3M.calculateMaturityFromEffective(SPOT_DATE, REF_DATA));
            double    factor            = MODEL.futuresConvexityFactor(MODEL_PARAMETERS, tradeLastTime, fixStartTime, fixEndTime);
            double    expectedFactor    = 1.000079130767980;

            assertEquals(expectedFactor, factor, TOLERANCE_RATE);
            // Derivative with respect to volatility parameters
            int nbSigma = MODEL_PARAMETERS.Volatility.size();
            ValueDerivatives factorDeriv = MODEL.futuresConvexityFactorAdjoint(MODEL_PARAMETERS, tradeLastTime, fixStartTime, fixEndTime);
            double           factor2     = factorDeriv.Value;

            double[] sigmaBar = factorDeriv.Derivatives.toArray();
            assertEquals(factor, factor2, TOLERANCE_RATE);
            double[] sigmaBarExpected = new double[nbSigma];
            double   shift            = 1E-6;

            for (int loops = 0; loops < nbSigma; loops++)
            {
                double[] volBumped = VOLATILITY.toArray();
                volBumped[loops] += shift;
                HullWhiteOneFactorPiecewiseConstantParameters parametersBumped = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volBumped), VOLATILITY_TIME);
                double factorPlus = MODEL.futuresConvexityFactor(parametersBumped, tradeLastTime, fixStartTime, fixEndTime);
                volBumped[loops] -= 2 * shift;
                parametersBumped  = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volBumped), VOLATILITY_TIME);
                double factorMinus = MODEL.futuresConvexityFactor(parametersBumped, tradeLastTime, fixStartTime, fixEndTime);
                sigmaBarExpected[loops] = (factorPlus - factorMinus) / (2 * shift);
                assertEquals(sigmaBarExpected[loops], sigmaBar[loops], TOLERANCE_RATE);
            }
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the beta parameter.
        /// <para>
        /// This is intended to be used in particular for Bermudan swaption first step of the pricing.
        /// </para>
        /// <para>
        /// Reference: Henrard, "M. Bermudan Swaptions in Gaussian HJM One-Factor Model: Analytical and Numerical Approaches".
        /// SSRN, October 2008. Available at SSRN: http://ssrn.com/abstract=1287982
        ///
        /// </para>
        /// </summary>
        /// <param name="data">  the Hull-White model data </param>
        /// <param name="startExpiry"> the start time of the expiry period </param>
        /// <param name="endExpiry">  the end time of the expiry period </param>
        /// <returns> the re-based bond volatility </returns>
        public double beta(HullWhiteOneFactorPiecewiseConstantParameters data, double startExpiry, double endExpiry)
        {
            double numerator  = 2 * data.MeanReversion;
            int    indexStart = 1;  // Period in which the time startExpiry is; volatilityTime.get(i-1) <= startExpiry < volatilityTime.get(i);

            while (startExpiry > data.VolatilityTime.get(indexStart))
            {
                indexStart++;
            }
            int indexEnd = indexStart;     // Period in which the time endExpiry is; volatilityTime.get(i-1) <= endExpiry < volatilityTime.get(i);

            while (endExpiry > data.VolatilityTime.get(indexEnd))
            {
                indexEnd++;
            }
            int sLen = indexEnd - indexStart + 1;

            double[] s = new double[sLen + 1];
            s[0] = startExpiry;
            Array.Copy(data.VolatilityTime.toArray(), indexStart, s, 1, sLen - 1);
            s[sLen] = endExpiry;
            double denominator = 0.0;

            for (int loopperiod = 0; loopperiod < sLen; loopperiod++)
            {
                denominator += data.Volatility.get(loopperiod + indexStart - 1) * data.Volatility.get(loopperiod + indexStart - 1) * (Math.Exp(2 * data.MeanReversion * s[loopperiod + 1]) - Math.Exp(2 * data.MeanReversion * s[loopperiod]));
            }
            return(Math.Sqrt(denominator / numerator));
        }
        /// <summary>
        /// Calculates the (zero-coupon) bond volatility divided by a bond numeraire, i.e., alpha, for a given period.
        /// </summary>
        /// <param name="data">  the Hull-White model data </param>
        /// <param name="startExpiry"> the start time of the expiry period </param>
        /// <param name="endExpiry">  the end time of the expiry period </param>
        /// <param name="numeraireTime">  the time to maturity for the bond numeraire </param>
        /// <param name="bondMaturity"> the time to maturity for the bond </param>
        /// <returns> the re-based bond volatility </returns>
        public double alpha(HullWhiteOneFactorPiecewiseConstantParameters data, double startExpiry, double endExpiry, double numeraireTime, double bondMaturity)
        {
            double factor1    = Math.Exp(-data.MeanReversion * numeraireTime) - Math.Exp(-data.MeanReversion * bondMaturity);
            double numerator  = 2 * data.MeanReversion * data.MeanReversion * data.MeanReversion;
            int    indexStart = Math.Abs(Arrays.binarySearch(data.VolatilityTime.toArray(), startExpiry) + 1);
            // Period in which the time startExpiry is; volatilityTime.get(i-1) <= startExpiry < volatilityTime.get(i);
            int indexEnd = Math.Abs(Arrays.binarySearch(data.VolatilityTime.toArray(), endExpiry) + 1);
            // Period in which the time endExpiry is; volatilityTime.get(i-1) <= endExpiry < volatilityTime.get(i);
            int sLen = indexEnd - indexStart + 1;

            double[] s = new double[sLen + 1];
            s[0] = startExpiry;
            Array.Copy(data.VolatilityTime.toArray(), indexStart, s, 1, sLen - 1);
            s[sLen] = endExpiry;
            double factor2 = 0d;

            double[] exp2as = new double[sLen + 1];
            for (int loopperiod = 0; loopperiod < sLen + 1; loopperiod++)
            {
                exp2as[loopperiod] = Math.Exp(2 * data.MeanReversion * s[loopperiod]);
            }
            for (int loopperiod = 0; loopperiod < sLen; loopperiod++)
            {
                factor2 += data.Volatility.get(loopperiod + indexStart - 1) * data.Volatility.get(loopperiod + indexStart - 1) * (exp2as[loopperiod + 1] - exp2as[loopperiod]);
            }
            return(factor1 * Math.Sqrt(factor2 / numerator));
        }
        /// <summary>
        /// Calculates the payment delay convexity factor used in coupons with mismatched dates pricing.
        /// </summary>
        /// <param name="parameters">  the Hull-White model parameters </param>
        /// <param name="startExpiry">  the start expiry time </param>
        /// <param name="endExpiry">  the end expiry time </param>
        /// <param name="u">  the fixing period start time </param>
        /// <param name="v">  the fixing period end time </param>
        /// <param name="tp">  the payment time </param>
        /// <returns> the factor </returns>
        public double paymentDelayConvexityFactor(HullWhiteOneFactorPiecewiseConstantParameters parameters, double startExpiry, double endExpiry, double u, double v, double tp)
        {
            double a          = parameters.MeanReversion;
            double factor1    = (Math.Exp(-a * v) - Math.Exp(-a * tp)) * (Math.Exp(-a * v) - Math.Exp(-a * u));
            double numerator  = 2 * a * a * a;
            int    indexStart = Math.Abs(Arrays.binarySearch(parameters.VolatilityTime.toArray(), startExpiry) + 1);
            // Period in which the time startExpiry is; volatilityTime.get(i-1) <= startExpiry < volatilityTime.get(i);
            int indexEnd = Math.Abs(Arrays.binarySearch(parameters.VolatilityTime.toArray(), endExpiry) + 1);
            // Period in which the time endExpiry is; volatilityTime.get(i-1) <= endExpiry < volatilityTime.get(i);
            int sLen = indexEnd - indexStart + 1;

            double[] s = new double[sLen + 1];
            s[0] = startExpiry;
            Array.Copy(parameters.VolatilityTime.toArray(), indexStart, s, 1, sLen - 1);
            s[sLen] = endExpiry;
            double factor2 = 0.0;

            double[] exp2as = new double[sLen + 1];
            for (int loopperiod = 0; loopperiod < sLen + 1; loopperiod++)
            {
                exp2as[loopperiod] = Math.Exp(2 * a * s[loopperiod]);
            }
            for (int loopperiod = 0; loopperiod < sLen; loopperiod++)
            {
                factor2 += parameters.Volatility.get(loopperiod + indexStart - 1) * parameters.Volatility.get(loopperiod + indexStart - 1) * (exp2as[loopperiod + 1] - exp2as[loopperiod]);
            }
            return(Math.Exp(factor1 * factor2 / numerator));
        }
Exemple #8
0
        //-------------------------------------------------------------------------
        public virtual void test_presentValueSensitivityHullWhiteParameter()
        {
            DoubleArray computedRec = PRICER.presentValueSensitivityModelParamsHullWhite(SWAPTION_REC_LONG, RATE_PROVIDER, HW_PROVIDER);
            DoubleArray computedPay = PRICER.presentValueSensitivityModelParamsHullWhite(SWAPTION_PAY_SHORT, RATE_PROVIDER, HW_PROVIDER);
            DoubleArray vols        = HW_PROVIDER.Parameters.Volatility;
            int         size        = vols.size();

            double[] expectedRec = new double[size];
            double[] expectedPay = new double[size];
            for (int i = 0; i < size; ++i)
            {
                double[] volsUp = vols.toArray();
                double[] volsDw = vols.toArray();
                volsUp[i] += FD_TOL;
                volsDw[i] -= FD_TOL;
                HullWhiteOneFactorPiecewiseConstantParameters         paramsUp = HullWhiteOneFactorPiecewiseConstantParameters.of(HW_PROVIDER.Parameters.MeanReversion, DoubleArray.copyOf(volsUp), HW_PROVIDER.Parameters.VolatilityTime.subArray(1, size));
                HullWhiteOneFactorPiecewiseConstantParameters         paramsDw = HullWhiteOneFactorPiecewiseConstantParameters.of(HW_PROVIDER.Parameters.MeanReversion, DoubleArray.copyOf(volsDw), HW_PROVIDER.Parameters.VolatilityTime.subArray(1, size));
                HullWhiteOneFactorPiecewiseConstantParametersProvider provUp   = HullWhiteOneFactorPiecewiseConstantParametersProvider.of(paramsUp, HW_PROVIDER.DayCount, HW_PROVIDER.ValuationDateTime);
                HullWhiteOneFactorPiecewiseConstantParametersProvider provDw   = HullWhiteOneFactorPiecewiseConstantParametersProvider.of(paramsDw, HW_PROVIDER.DayCount, HW_PROVIDER.ValuationDateTime);
                expectedRec[i] = 0.5 * (PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, provUp).Amount - PRICER.presentValue(SWAPTION_REC_LONG, RATE_PROVIDER, provDw).Amount) / FD_TOL;
                expectedPay[i] = 0.5 * (PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, provUp).Amount - PRICER.presentValue(SWAPTION_PAY_SHORT, RATE_PROVIDER, provDw).Amount) / FD_TOL;
            }
            assertTrue(DoubleArrayMath.fuzzyEquals(computedRec.toArray(), expectedRec, NOTIONAL * FD_TOL));
            assertTrue(DoubleArrayMath.fuzzyEquals(computedPay.toArray(), expectedPay, NOTIONAL * FD_TOL));
        }
        /// <summary>
        /// Tests the equal and hash code methods.
        /// </summary>
        public virtual void equalHash()
        {
            HullWhiteOneFactorPiecewiseConstantParameters newParameter = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, VOLATILITY, VOLATILITY_TIME);

            assertTrue(MODEL_PARAMETERS.Equals(newParameter));
            assertTrue(MODEL_PARAMETERS.GetHashCode() == newParameter.GetHashCode());
            HullWhiteOneFactorPiecewiseConstantParameters modifiedParameter = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION + 0.01, VOLATILITY, VOLATILITY_TIME);

            assertFalse(MODEL_PARAMETERS.Equals(modifiedParameter));
        }
        /// <summary>
        /// Tests the class setters.
        /// </summary>
        public virtual void setter()
        {
            double volReplaced = 0.02;
            HullWhiteOneFactorPiecewiseConstantParameters param1 = MODEL_PARAMETERS.withLastVolatility(volReplaced);

            assertEquals(volReplaced, param1.Volatility.get(param1.Volatility.size() - 1));
            HullWhiteOneFactorPiecewiseConstantParameters param2 = MODEL_PARAMETERS.withLastVolatility(VOLATILITY.get(VOLATILITY.size() - 1));

            for (int loopperiod = 0; loopperiod < param2.Volatility.size(); loopperiod++)
            {
                assertEquals(VOLATILITY.get(loopperiod), param2.Volatility.get(loopperiod));
            }
        }
        /// <summary>
        /// Test the payment delay convexity adjustment factor. Analysis of the size.
        /// In normal test, should have (enabled=false)
        /// </summary>
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test(enabled = false) public void paymentDelayConvexityFactorAnalysis()
        public virtual void paymentDelayConvexityFactorAnalysis()
        {
            double hwMeanReversion = 0.01;
            double rate            = 0.02;

            double[] tenorTime = new double[] { 0.25, 0.50 };
            int      nbTenors  = tenorTime.Length;

            double[] lagPayTime     = new double[] { 1.0d / 365.0d, 2.0d / 365.0d, 7.0d / 365.0d };
            int      nbLags         = lagPayTime.Length;
            double   lagFixTime     = 2.0d / 365.0d;
            int      nbPeriods      = 120;
            double   startTimeFirst = 0.25;
            double   startTimeStep  = 0.25;

            double[] startTime = new double[nbPeriods];
            for (int loopp = 0; loopp < nbPeriods; loopp++)
            {
                startTime[loopp] = startTimeFirst + loopp * startTimeStep;
            }

            // Constant volatility
            double hwEta = 0.02;
            HullWhiteOneFactorPiecewiseConstantParameters parameters = HullWhiteOneFactorPiecewiseConstantParameters.of(hwMeanReversion, DoubleArray.of(hwEta), DoubleArray.of(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[][][] factor = new double[nbTenors][nbLags][nbPeriods];
            double[][][] factor = RectangularArrays.ReturnRectangularDoubleArray(nbTenors, nbLags, nbPeriods);
//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[][][] adj = new double[nbTenors][nbLags][nbPeriods];
            double[][][] adj = RectangularArrays.ReturnRectangularDoubleArray(nbTenors, nbLags, nbPeriods);
            for (int loopt = 0; loopt < nbTenors; loopt++)
            {
                for (int loopl = 0; loopl < nbLags; loopl++)
                {
                    for (int loopp = 0; loopp < nbPeriods; loopp++)
                    {
                        factor[loopt][loopl][loopp] = MODEL.paymentDelayConvexityFactor(parameters, 0, startTime[loopp] - lagFixTime, startTime[loopp], startTime[loopp] + tenorTime[loopt], startTime[loopp] + tenorTime[loopt] - lagPayTime[loopl]);
                        adj[loopt][loopl][loopp]    = (1.0d / tenorTime[loopt] - rate) * (factor[loopt][loopl][loopp] - 1);
                    }
                }
            }

//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @SuppressWarnings("unused") int t = 0;
            int t = 0;

            t++;
        }
        /// <summary>
        /// Calculates the maturity dependent part of the volatility (function called H in the implementation note).
        /// </summary>
        /// <param name="hwParameters">  the model parameters </param>
        /// <param name="u">  the start time </param>
        /// <param name="v">  the end time </param>
        /// <returns> the volatility </returns>
        public DoubleMatrix volatilityMaturityPart(HullWhiteOneFactorPiecewiseConstantParameters hwParameters, double u, DoubleMatrix v)
        {
            double a = hwParameters.MeanReversion;

            double[][] result = new double[v.rowCount()][];
            double     expau  = Math.Exp(-a * u);

            for (int loopcf1 = 0; loopcf1 < v.rowCount(); loopcf1++)
            {
                DoubleArray vRow = v.row(loopcf1);
                result[loopcf1] = new double[vRow.size()];
                for (int loopcf2 = 0; loopcf2 < vRow.size(); loopcf2++)
                {
                    result[loopcf1][loopcf2] = (expau - Math.Exp(-a * vRow.get(loopcf2))) / a;
                }
            }
            return(DoubleMatrix.copyOf(result));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the future convexity factor used in future pricing.
        /// <para>
        /// The factor is called gamma in the reference:
        /// Henrard, M. "The Irony in the derivatives discounting Part II: the crisis", Wilmott Journal, 2010, 2, 301-316
        ///
        /// </para>
        /// </summary>
        /// <param name="data">  the Hull-White model parameters </param>
        /// <param name="t0">  the first expiry time </param>
        /// <param name="t1">  the first reference time </param>
        /// <param name="t2">  the second reference time </param>
        /// <returns> the factor </returns>
        public double futuresConvexityFactor(HullWhiteOneFactorPiecewiseConstantParameters data, double t0, double t1, double t2)
        {
            double factor1   = Math.Exp(-data.MeanReversion * t1) - Math.Exp(-data.MeanReversion * t2);
            double numerator = 2 * data.MeanReversion * data.MeanReversion * data.MeanReversion;
            int    indexT0   = 1; // Period in which the time t0 is; volatilityTime[i-1] <= t0 < volatilityTime[i];

            while (t0 > data.VolatilityTime.get(indexT0))
            {
                indexT0++;
            }
            double[] s = new double[indexT0 + 1];
            Array.Copy(data.VolatilityTime.toArray(), 0, s, 0, indexT0);
            s[indexT0] = t0;
            double factor2 = 0.0;

            for (int loopperiod = 0; loopperiod < indexT0; loopperiod++)
            {
                factor2 += data.Volatility.get(loopperiod) * data.Volatility.get(loopperiod) * (Math.Exp(data.MeanReversion * s[loopperiod + 1]) - Math.Exp(data.MeanReversion * s[loopperiod])) * (2 - Math.Exp(-data.MeanReversion * (t2 - s[loopperiod + 1])) - Math.Exp(-data.MeanReversion * (t2 - s[loopperiod])));
            }
            return(Math.Exp(factor1 / numerator * factor2));
        }
        /// <summary>
        /// Calculates the (zero-coupon) bond volatility divided by a bond numeraire, i.e., alpha, for a given period and
        /// its derivatives.
        /// <para>
        /// The derivative values are the derivatives of the function alpha with respect to the piecewise constant volatilities.
        ///
        /// </para>
        /// </summary>
        /// <param name="data">  the Hull-White model data </param>
        /// <param name="startExpiry">  the start time of the expiry period </param>
        /// <param name="endExpiry">  the end time of the expiry period </param>
        /// <param name="numeraireTime">  the time to maturity for the bond numeraire </param>
        /// <param name="bondMaturity">  the time to maturity for the bond </param>
        /// <returns> The re-based bond volatility </returns>
        public ValueDerivatives alphaAdjoint(HullWhiteOneFactorPiecewiseConstantParameters data, double startExpiry, double endExpiry, double numeraireTime, double bondMaturity)
        {
            // Forward sweep
            double factor1    = Math.Exp(-data.MeanReversion * numeraireTime) - Math.Exp(-data.MeanReversion * bondMaturity);
            double numerator  = 2 * data.MeanReversion * data.MeanReversion * data.MeanReversion;
            int    indexStart = Math.Abs(Arrays.binarySearch(data.VolatilityTime.toArray(), startExpiry) + 1);
            // Period in which the time startExpiry is; volatilityTime.get(i-1) <= startExpiry < volatilityTime.get(i);
            int indexEnd = Math.Abs(Arrays.binarySearch(data.VolatilityTime.toArray(), endExpiry) + 1);
            // Period in which the time endExpiry is; volatilityTime.get(i-1) <= endExpiry < volatilityTime.get(i);
            int sLen = indexEnd - indexStart + 1;

            double[] s = new double[sLen + 1];
            s[0] = startExpiry;
            Array.Copy(data.VolatilityTime.toArray(), indexStart, s, 1, sLen - 1);
            s[sLen] = endExpiry;
            double factor2 = 0.0;

            double[] exp2as = new double[sLen + 1];
            for (int loopperiod = 0; loopperiod < sLen + 1; loopperiod++)
            {
                exp2as[loopperiod] = Math.Exp(2 * data.MeanReversion * s[loopperiod]);
            }
            for (int loopperiod = 0; loopperiod < sLen; loopperiod++)
            {
                factor2 += data.Volatility.get(loopperiod + indexStart - 1) * data.Volatility.get(loopperiod + indexStart - 1) * (exp2as[loopperiod + 1] - exp2as[loopperiod]);
            }
            double sqrtFactor2Num = Math.Sqrt(factor2 / numerator);
            double alpha          = factor1 * sqrtFactor2Num;
            // Backward sweep
            double alphaBar   = 1.0;
            double factor2Bar = factor1 / sqrtFactor2Num / 2.0 / numerator * alphaBar;

            double[] derivatives = new double[data.Volatility.size()];
            for (int loopperiod = 0; loopperiod < sLen; loopperiod++)
            {
                derivatives[loopperiod + indexStart - 1] = 2 * data.Volatility.get(loopperiod + indexStart - 1) * (exp2as[loopperiod + 1] - exp2as[loopperiod]) * factor2Bar;
            }
            return(ValueDerivatives.of(alpha, DoubleArray.ofUnsafe(derivatives)));
        }