/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.interestrate; import java.util.Arrays; import com.opengamma.analytics.financial.model.interestrate.definition.HullWhiteOneFactorPiecewiseConstantParameters; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.rootfinding.BracketRoot; import com.opengamma.analytics.math.rootfinding.RidderSingleRootFinder; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.ObjectsPair; import com.opengamma.util.tuple.Pair; /** * Methods related to the Hull-White one factor (extended Vasicek) model with piecewise constant volatility. */ public class HullWhiteOneFactorPiecewiseConstantInterestRateModel { /** * Computes the future convexity factor used in future pricing. The factor * is called $\gamma$ in the article and is given by * $$ * \begin{equation*} * \gamma(t) = \exp\left(\int_t^{t_0} \nu(s,t_2) (\nu(s,t_2)-\nu(s,t_1)) ds \right). * \end{equation*} * $$ * <p> * Reference: Henrard, M. The Irony in the derivatives discounting Part II: the crisis. Wilmott Journal, 2010, 2, 301-316 * @param data The Hull-White model parameters. * @param t0 The first expiry time. * @param t1 The first reference time. * @param t2 The second reference time. * @return The factor. */ public double futuresConvexityFactor(final HullWhiteOneFactorPiecewiseConstantParameters data, double t0, double t1, double t2) { double factor1 = Math.exp(-data.getMeanReversion() * t1) - Math.exp(-data.getMeanReversion() * t2); double numerator = 2 * data.getMeanReversion() * data.getMeanReversion() * data.getMeanReversion(); int indexT0 = 1; // Period in which the time t0 is; _volatilityTime[i-1] <= t0 < _volatilityTime[i]; while (t0 > data.getVolatilityTime()[indexT0]) { indexT0++; } double[] s = new double[indexT0 + 1]; System.arraycopy(data.getVolatilityTime(), 0, s, 0, indexT0); s[indexT0] = t0; double factor2 = 0.0; for (int loopperiod = 0; loopperiod < indexT0; loopperiod++) { factor2 += data.getVolatility()[loopperiod] * data.getVolatility()[loopperiod] * (Math.exp(data.getMeanReversion() * s[loopperiod + 1]) - Math.exp(data.getMeanReversion() * s[loopperiod])) * (2 - Math.exp(-data.getMeanReversion() * (t2 - s[loopperiod + 1])) - Math.exp(-data.getMeanReversion() * (t2 - s[loopperiod]))); } return Math.exp(factor1 / numerator * factor2); } /** * Computes the future convexity factor used in future pricing. Computes also the derivatives of the factor with respect to the model volatilities. * The factor is called $\gamma$ in the article and is given by * $$ * \begin{equation*} * \gamma(t) = \exp\left(\int_t^{t_0} \nu(s,t_2) (\nu(s,t_2)-\nu(s,t_1)) ds \right). * \end{equation*} * $$ * <p> * Reference: Henrard, M. The Irony in the derivatives discounting Part II: the crisis. Wilmott Journal, 2010, 2, 301-316 * @param data The Hull-White model parameters. * @param t0 The expiry time. * @param t1 The first reference time. * @param t2 The second reference time. * @param derivatives Array used for return the derivatives with respect to the input. The array is changed by the method. The derivatives of the function alpha * with respect to the piecewise constant volatilities. * @return The factor. */ public double futuresConvexityFactor(final HullWhiteOneFactorPiecewiseConstantParameters data, final double t0, final double t1, final double t2, final double[] derivatives) { final int nbSigma = data.getVolatility().length; ArgumentChecker.isTrue(derivatives.length == nbSigma, "derivatives vector of incorrect size"); double factor1 = Math.exp(-data.getMeanReversion() * t1) - Math.exp(-data.getMeanReversion() * t2); double numerator = 2 * data.getMeanReversion() * data.getMeanReversion() * data.getMeanReversion(); int indexT0 = 1; // Period in which the time t0 is; _volatilityTime[i-1] <= t0 < _volatilityTime[i]; while (t0 > data.getVolatilityTime()[indexT0]) { indexT0++; } double[] s = new double[indexT0 + 1]; System.arraycopy(data.getVolatilityTime(), 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.getMeanReversion() * s[loopperiod + 1]) - Math.exp(data.getMeanReversion() * s[loopperiod])) * (2 - Math.exp(-data.getMeanReversion() * (t2 - s[loopperiod + 1])) - Math.exp(-data.getMeanReversion() * (t2 - s[loopperiod]))); factor2 += data.getVolatility()[loopperiod] * data.getVolatility()[loopperiod] * factorExp[loopperiod]; } double factor = Math.exp(factor1 / numerator * factor2); // Backward sweep double factorBar = 1.0; double factor2Bar = factor1 / numerator * factor * factorBar; for (int loopperiod = 0; loopperiod < indexT0; loopperiod++) { derivatives[loopperiod] = 2 * data.getVolatility()[loopperiod] * factorExp[loopperiod] * factor2Bar; } return factor; } /** * Computes the payment delay convexity factor used in coupons with mismatched dates pricing. The factor * is called $\zeta$ in the note and is given by * $$ * \begin{equation*} * \zeta = \exp\left(\int_{\theta_0}^{\theta_1} (\nu(s,v)-\nu(s,t_p)) (\nu(s,v)-\nu(s,u)) ds \right). * \end{equation*} * $$ * <p> * Reference: Henrard, M. xxx * @param parameters The Hull-White model parameters. * @param startExpiry The start expiry time. * @param endExpiry The end expiry time. * @param u The fixing period start time. * @param v The fixing period end time. * @param tp The payment time. * @return The factor. */ public double paymentDelayConvexityFactor(final HullWhiteOneFactorPiecewiseConstantParameters parameters, final double startExpiry, final double endExpiry, final double u, final double v, final double tp) { final double a = parameters.getMeanReversion(); final double factor1 = (Math.exp(-a * v) - Math.exp(-a * tp)) * (Math.exp(-a * v) - Math.exp(-a * u)); final double numerator = 2 * a * a * a; int indexStart = Math.abs(Arrays.binarySearch(parameters.getVolatilityTime(), startExpiry) + 1); // Period in which the time startExpiry is; _volatilityTime[i-1] <= startExpiry < _volatilityTime[i]; int indexEnd = Math.abs(Arrays.binarySearch(parameters.getVolatilityTime(), endExpiry) + 1); // Period in which the time endExpiry is; _volatilityTime[i-1] <= endExpiry < _volatilityTime[i]; int sLen = indexEnd - indexStart + 1; double[] s = new double[sLen + 1]; s[0] = startExpiry; System.arraycopy(parameters.getVolatilityTime(), 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.getVolatility()[loopperiod + indexStart - 1] * parameters.getVolatility()[loopperiod + indexStart - 1] * (exp2as[loopperiod + 1] - exp2as[loopperiod]); } return Math.exp(factor1 * factor2 / numerator); } /** * Computes the (zero-coupon) bond volatility divided by a bond numeraire for a given period. * @param data Hull-White model data. * @param startExpiry Start time of the expiry period. * @param endExpiry End time of the expiry period. * @param numeraireTime Time to maturity for the bond numeraire. * @param bondMaturity Time to maturity for the bond. * @return The re-based bond volatility. */ public double alpha(final HullWhiteOneFactorPiecewiseConstantParameters data, final double startExpiry, final double endExpiry, final double numeraireTime, final double bondMaturity) { double factor1 = Math.exp(-data.getMeanReversion() * numeraireTime) - Math.exp(-data.getMeanReversion() * bondMaturity); double numerator = 2 * data.getMeanReversion() * data.getMeanReversion() * data.getMeanReversion(); int indexStart = Math.abs(Arrays.binarySearch(data.getVolatilityTime(), startExpiry) + 1); // Period in which the time startExpiry is; _volatilityTime[i-1] <= startExpiry < _volatilityTime[i]; int indexEnd = Math.abs(Arrays.binarySearch(data.getVolatilityTime(), endExpiry) + 1); // Period in which the time endExpiry is; _volatilityTime[i-1] <= endExpiry < _volatilityTime[i]; int sLen = indexEnd - indexStart + 1; double[] s = new double[sLen + 1]; s[0] = startExpiry; System.arraycopy(data.getVolatilityTime(), 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.getMeanReversion() * s[loopperiod]); } for (int loopperiod = 0; loopperiod < sLen; loopperiod++) { factor2 += data.getVolatility()[loopperiod + indexStart - 1] * data.getVolatility()[loopperiod + indexStart - 1] * (exp2as[loopperiod + 1] - exp2as[loopperiod]); } return factor1 * Math.sqrt(factor2 / numerator); } /** * The adjoint version of the method. Computes the (zero-coupon) bond volatility divided by a bond numeraire for a given period ant its derivatives. * @param data Hull-White model data. * @param startExpiry Start time of the expiry period. * @param endExpiry End time of the expiry period. * @param numeraireTime Time to maturity for the bond numeraire. * @param bondMaturity Time to maturity for the bond. * @param derivatives Array used for return the derivatives with respect to the input. The array is changed by the method. The derivatives of the function alpha * with respect to the piecewise constant volatilities. * @return The re-based bond volatility. */ public double alpha(final HullWhiteOneFactorPiecewiseConstantParameters data, final double startExpiry, final double endExpiry, final double numeraireTime, final double bondMaturity, double[] derivatives) { int nbSigma = data.getVolatility().length; for (int loopperiod = 0; loopperiod < nbSigma; loopperiod++) { // To clean derivatives derivatives[loopperiod] = 0.0; } // Forward sweep double factor1 = Math.exp(-data.getMeanReversion() * numeraireTime) - Math.exp(-data.getMeanReversion() * bondMaturity); double numerator = 2 * data.getMeanReversion() * data.getMeanReversion() * data.getMeanReversion(); int indexStart = Math.abs(Arrays.binarySearch(data.getVolatilityTime(), startExpiry) + 1); // Period in which the time startExpiry is; _volatilityTime[i-1] <= startExpiry < _volatilityTime[i]; int indexEnd = Math.abs(Arrays.binarySearch(data.getVolatilityTime(), endExpiry) + 1); // Period in which the time endExpiry is; _volatilityTime[i-1] <= endExpiry < _volatilityTime[i]; int sLen = indexEnd - indexStart + 1; double[] s = new double[sLen + 1]; s[0] = startExpiry; System.arraycopy(data.getVolatilityTime(), 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.getMeanReversion() * s[loopperiod]); } for (int loopperiod = 0; loopperiod < sLen; loopperiod++) { factor2 += data.getVolatility()[loopperiod + indexStart - 1] * data.getVolatility()[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; for (int loopperiod = 0; loopperiod < sLen; loopperiod++) { derivatives[loopperiod + indexStart - 1] = 2 * data.getVolatility()[loopperiod + indexStart - 1] * (exp2as[loopperiod + 1] - exp2as[loopperiod]) * factor2Bar; } return alpha; } /** * Computes the exercise boundary for swaptions. * Reference: Henrard, M. (2003). Explicit bond option and swaption formula in Heath-Jarrow-Morton one-factor model. International Journal of Theoretical and Applied Finance, 6(1):57--72. * @param discountedCashFlow The cash flow equivalent discounted to today. * @param alpha The zero-coupon bond volatilities. * @return The exercise boundary. */ public double kappa(final double[] discountedCashFlow, final double[] alpha) { final Function1D<Double, Double> swapValue = new Function1D<Double, Double>() { @Override public Double evaluate(final Double x) { double error = 0.0; for (int loopcf = 0; loopcf < alpha.length; loopcf++) { error += discountedCashFlow[loopcf] * Math.exp(-0.5 * alpha[loopcf] * alpha[loopcf] - (alpha[loopcf] - alpha[0]) * x); } return error; } }; final BracketRoot bracketer = new BracketRoot(); double accuracy = 1.0E-8; final RidderSingleRootFinder rootFinder = new RidderSingleRootFinder(accuracy); final double[] range = bracketer.getBracketedPoints(swapValue, -2.0, 2.0); return rootFinder.getRoot(swapValue, range[0], range[1]); } public double beta(final HullWhiteOneFactorPiecewiseConstantParameters data, final double startExpiry, final double endExpiry) { double numerator = 2 * data.getMeanReversion(); int indexStart = 1; // Period in which the time startExpiry is; _volatilityTime[i-1] <= startExpiry < _volatilityTime[i]; while (startExpiry > data.getVolatilityTime()[indexStart]) { indexStart++; } int indexEnd = indexStart; // Period in which the time endExpiry is; _volatilityTime[i-1] <= endExpiry < _volatilityTime[i]; while (endExpiry > data.getVolatilityTime()[indexEnd]) { indexEnd++; } int sLen = indexEnd - indexStart + 1; double[] s = new double[sLen + 1]; s[0] = startExpiry; System.arraycopy(data.getVolatilityTime(), indexStart, s, 1, sLen - 1); s[sLen] = endExpiry; double denominator = 0.0; for (int loopperiod = 0; loopperiod < sLen; loopperiod++) { denominator += data.getVolatility()[loopperiod + indexStart - 1] * data.getVolatility()[loopperiod + indexStart - 1] * (Math.exp(2 * data.getMeanReversion() * s[loopperiod + 1]) - Math.exp(2 * data.getMeanReversion() * s[loopperiod])); } return Math.sqrt(denominator / numerator); } /** * Compute the common part of the exercise boundary of European swaptions * forward. Used in particular for Bermudan swaption first step of the * pricing. * <p> * 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 * @param discountedCashFlow The swap discounted cash flows. * @param alpha2 The $\alpha^2$ parameters. * @param hwH The H factors. * @return The exercise boundary. */ public double lambda(final double[] discountedCashFlow, final double[] alpha2, final double[] hwH) { final Function1D<Double, Double> swapValue = new Function1D<Double, Double>() { @Override public Double evaluate(final Double x) { double value = 0.0; for (int loopcf = 0; loopcf < alpha2.length; loopcf++) { value += discountedCashFlow[loopcf] * Math.exp(-0.5 * alpha2[loopcf] - hwH[loopcf] * x); } return value; } }; final BracketRoot bracketer = new BracketRoot(); double accuracy = 1.0E-8; final RidderSingleRootFinder rootFinder = new RidderSingleRootFinder(accuracy); final double[] range = bracketer.getBracketedPoints(swapValue, -2.0, 2.0); return rootFinder.getRoot(swapValue, range[0], range[1]); } /** * The maturity dependent part of the volatility (function called H in the implementation note). * @param hwParameters The model parameters. * @param u The start time. * @param v The end times. * @return The volatility. Same dimension as v. */ public double[][] volatilityMaturityPart(final HullWhiteOneFactorPiecewiseConstantParameters hwParameters, double u, double[][] v) { double a = hwParameters.getMeanReversion(); double[][] result = new double[v.length][]; double expau = Math.exp(-a * u); for (int loopcf1 = 0; loopcf1 < v.length; loopcf1++) { result[loopcf1] = new double[v[loopcf1].length]; for (int loopcf2 = 0; loopcf2 < v[loopcf1].length; loopcf2++) { result[loopcf1][loopcf2] = (expau - Math.exp(-a * v[loopcf1][loopcf2])) / a; } } return result; } /** * The expiry time dependent part of the volatility. * @param hwParameters The model parameters. * @param theta0 The start expiry time. * @param theta1 The end expiry time. * @return The volatility. */ public double gamma(final HullWhiteOneFactorPiecewiseConstantParameters hwParameters, double theta0, double theta1) { double a = hwParameters.getMeanReversion(); double[] sigma = hwParameters.getVolatility(); int indexStart = 1; // Period in which the time startExpiry is; _volatilityTime[i-1] <= startExpiry < _volatilityTime[i]; while (theta0 > hwParameters.getVolatilityTime()[indexStart]) { indexStart++; } int indexEnd = indexStart; // Period in which the time endExpiry is; _volatilityTime[i-1] <= endExpiry < _volatilityTime[i]; while (theta1 > hwParameters.getVolatilityTime()[indexEnd]) { indexEnd++; } int sLen = indexEnd - indexStart + 2; double[] s = new double[sLen]; s[0] = theta0; System.arraycopy(hwParameters.getVolatilityTime(), indexStart, s, 1, sLen - 2); s[sLen - 1] = theta1; double gamma = 0.0; double[] exp2as = new double[sLen]; for (int loopindex = 0; loopindex < sLen; loopindex++) { exp2as[loopindex] = Math.exp(2 * a * s[loopindex]); } for (int loopindex = 0; loopindex < sLen - 1; loopindex++) { gamma += sigma[indexStart - 1 + loopindex] * sigma[indexStart - 1 + loopindex] * (exp2as[loopindex + 1] - exp2as[loopindex]); } return gamma; } /** * Compute the swap rate for a given value of the standard normal random variable in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate. */ public double swapRate(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { ArgumentChecker.isTrue(discountedCashFlowFixed.length == alphaFixed.length, "Length shouyld be equal"); double numerator = 0.0; for (int loopcf = 0; loopcf < discountedCashFlowIbor.length; loopcf++) { numerator += discountedCashFlowIbor[loopcf] * Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]); } double denominator = 0.0; for (int loopcf = 0; loopcf < discountedCashFlowFixed.length; loopcf++) { denominator += discountedCashFlowFixed[loopcf] * Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); } return -numerator / denominator; } /** * Compute the first order derivative of the swap rate with respect to the value of the standard normal random variable in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate. */ public double swapRateDx1(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { double f = 0.0; double df = 0.0; double term; for (int loopcf = 0; loopcf < discountedCashFlowIbor.length; loopcf++) { term = discountedCashFlowIbor[loopcf] * Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]); f += term; df += -alphaIbor[loopcf] * term; } double g = 0.0; double dg = 0.0; for (int loopcf = 0; loopcf < discountedCashFlowFixed.length; loopcf++) { term = discountedCashFlowFixed[loopcf] * Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); g += term; dg += -alphaFixed[loopcf] * term; } return -(df * g - f * dg) / (g * g); } /** * Computes the second order derivative of the swap rate with respect to the value of the standard normal random variable in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate. */ public double swapRateDx2(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { double f = 0.0; double df = 0.0; double df2 = 0.0; double term; for (int loopcf = 0; loopcf < discountedCashFlowIbor.length; loopcf++) { term = discountedCashFlowIbor[loopcf] * Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]); f += term; df += -alphaIbor[loopcf] * term; df2 += alphaIbor[loopcf] * alphaIbor[loopcf] * term; } double g = 0.0; double dg = 0.0; double dg2 = 0.0; for (int loopcf = 0; loopcf < discountedCashFlowFixed.length; loopcf++) { term = discountedCashFlowFixed[loopcf] * Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); g += term; dg += -alphaFixed[loopcf] * term; dg2 += alphaFixed[loopcf] * alphaFixed[loopcf] * term; } double g2 = g * g; double g3 = g * g2; return -df2 / g + (2 * df * dg + f * dg2) / g2 - 2 * f * dg * dg / g3; } /** * Compute the first order derivative of the swap rate with respect to the discountedCashFlowIbor in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate derivative. */ public double[] swapRateDdcfi1(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { final int nbDcfi = discountedCashFlowIbor.length; final int nbDcff = discountedCashFlowFixed.length; final double[] swapRateDdcfi1 = new double[nbDcfi]; double denominator = 0.0; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { denominator += discountedCashFlowFixed[loopcf] * Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); } for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { swapRateDdcfi1[loopcf] = -Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]) / denominator; } return swapRateDdcfi1; } /** * Compute the first order derivative of the swap rate with respect to the discountedCashFlowFixed in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate derivative. */ public double[] swapRateDdcff1(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { final int nbDcff = discountedCashFlowFixed.length; final int nbDcfi = discountedCashFlowIbor.length; final double[] expD = new double[nbDcfi]; double numerator = 0.0; for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { numerator += discountedCashFlowIbor[loopcf] * Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]); } double denominator = 0.0; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { expD[loopcf] = Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); denominator += discountedCashFlowFixed[loopcf] * expD[loopcf]; } final double ratio = numerator / (denominator * denominator); final double[] swapRateDdcff1 = new double[nbDcff]; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { swapRateDdcff1[loopcf] = ratio * expD[loopcf]; } return swapRateDdcff1; } /** * Compute the first order derivative of the swap rate with respect to the alphaIbor in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate derivatives. */ public double[] swapRateDai1(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { final int nbDcfi = discountedCashFlowIbor.length; final int nbDcff = discountedCashFlowFixed.length; final double[] swapRateDai1 = new double[nbDcfi]; double denominator = 0.0; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { denominator += discountedCashFlowFixed[loopcf] * Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); } for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { swapRateDai1[loopcf] = discountedCashFlowIbor[loopcf] * Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]) * (x + alphaIbor[loopcf]) / denominator; } return swapRateDai1; } /** * Compute the first order derivative of the swap rate with respect to the alphaFixed in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate derivatives. */ public double[] swapRateDaf1(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { final int nbDcff = discountedCashFlowFixed.length; final int nbDcfi = discountedCashFlowIbor.length; final double[] expD = new double[nbDcfi]; double numerator = 0.0; for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { numerator += discountedCashFlowIbor[loopcf] * Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]); } double denominator = 0.0; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { expD[loopcf] = discountedCashFlowFixed[loopcf] * Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); denominator += expD[loopcf]; } final double ratio = numerator / (denominator * denominator); final double[] swapRateDaf1 = new double[nbDcff]; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { swapRateDaf1[loopcf] = ratio * expD[loopcf] * (-x - alphaFixed[loopcf]); } return swapRateDaf1; } /** * Compute the first order derivative with respect to the discountedCashFlowFixed and to the discountedCashFlowIbor of the of swap rate second derivative with respect * to the random variable x in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate derivative. Made of a pair of arrays. The first one is the derivative wrt discountedCashFlowFixed and the second one wrt discountedCashFlowIbor. */ public Pair<double[], double[]> swapRateDx2Ddcf1(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { final int nbDcff = discountedCashFlowFixed.length; final int nbDcfi = discountedCashFlowIbor.length; double f = 0.0; double df = 0.0; double df2 = 0.0; double[] termIbor = new double[nbDcfi]; double[] expIbor = new double[nbDcfi]; for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { expIbor[loopcf] = Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]); termIbor[loopcf] = discountedCashFlowIbor[loopcf] * expIbor[loopcf]; f += termIbor[loopcf]; df += -alphaIbor[loopcf] * termIbor[loopcf]; df2 += alphaIbor[loopcf] * alphaIbor[loopcf] * termIbor[loopcf]; } double g = 0.0; double dg = 0.0; double dg2 = 0.0; double[] termFixed = new double[nbDcff]; double[] expFixed = new double[nbDcff]; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { expFixed[loopcf] = Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); termFixed[loopcf] = discountedCashFlowFixed[loopcf] * expFixed[loopcf]; g += termFixed[loopcf]; dg += -alphaFixed[loopcf] * termFixed[loopcf]; dg2 += alphaFixed[loopcf] * alphaFixed[loopcf] * termFixed[loopcf]; } double g2 = g * g; double g3 = g * g2; double g4 = g * g3; // double dx2 = -((df2 * g - f * dg2) / g2 - (df * g - f * dg) * 2 * dg / g3); // Backward sweep double dx2Bar = 1.0; double gBar = (df2 / g2 - 2 * f * dg2 / g3 - 4 * df * dg / g3 + 6 * dg * dg * f / g4) * dx2Bar; double dgBar = (2 * df / g2 - 4 * f * dg / g3) * dx2Bar; double dg2Bar = f / g2 * dx2Bar; double fBar = (dg2 / g2 - 2 * dg * dg / g3) * dx2Bar; double dfBar = 2 * dg / g2 * dx2Bar; double df2Bar = -1.0 / g * dx2Bar; final double[] discountedCashFlowFixedBar = new double[nbDcff]; final double[] termFixedBar = new double[nbDcff]; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { termFixedBar[loopcf] = gBar - alphaFixed[loopcf] * dgBar + alphaFixed[loopcf] * alphaFixed[loopcf] * dg2Bar; discountedCashFlowFixedBar[loopcf] = expFixed[loopcf] * termFixedBar[loopcf]; } final double[] discountedCashFlowIborBar = new double[nbDcfi]; final double[] termIborBar = new double[nbDcfi]; for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { termIborBar[loopcf] = fBar - alphaIbor[loopcf] * dfBar + alphaIbor[loopcf] * alphaIbor[loopcf] * df2Bar; discountedCashFlowIborBar[loopcf] = expIbor[loopcf] * termIborBar[loopcf]; } return ObjectsPair.of(discountedCashFlowFixedBar, discountedCashFlowIborBar); } /** * Compute the first order derivative with respect to the alphaFixed and to the alphaIbor of the of swap rate second derivative with respect * to the random variable x in the $P(.,\theta)$ numeraire. * @param x The random variable value. * @param discountedCashFlowFixed The discounted cash flows equivalent of the swap fixed leg. * @param alphaFixed The zero-coupon bond volatilities for the swap fixed leg. * @param discountedCashFlowIbor The discounted cash flows equivalent of the swap Ibor leg. * @param alphaIbor The zero-coupon bond volatilities for the swap Ibor leg. * @return The swap rate derivatives. Made of a pair of arrays. The first one is the derivative wrt alphaFixed and the second one wrt alphaIbor. */ public Pair<double[], double[]> swapRateDx2Da1(final double x, final double[] discountedCashFlowFixed, final double[] alphaFixed, final double[] discountedCashFlowIbor, final double[] alphaIbor) { final int nbDcff = discountedCashFlowFixed.length; final int nbDcfi = discountedCashFlowIbor.length; double f = 0.0; double df = 0.0; double df2 = 0.0; double[] termIbor = new double[nbDcfi]; double[] expIbor = new double[nbDcfi]; for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { expIbor[loopcf] = Math.exp(-alphaIbor[loopcf] * x - 0.5 * alphaIbor[loopcf] * alphaIbor[loopcf]); termIbor[loopcf] = discountedCashFlowIbor[loopcf] * expIbor[loopcf]; f += termIbor[loopcf]; df += -alphaIbor[loopcf] * termIbor[loopcf]; df2 += alphaIbor[loopcf] * alphaIbor[loopcf] * termIbor[loopcf]; } double g = 0.0; double dg = 0.0; double dg2 = 0.0; double[] termFixed = new double[nbDcff]; double[] expFixed = new double[nbDcff]; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { expFixed[loopcf] = Math.exp(-alphaFixed[loopcf] * x - 0.5 * alphaFixed[loopcf] * alphaFixed[loopcf]); termFixed[loopcf] = discountedCashFlowFixed[loopcf] * expFixed[loopcf]; g += termFixed[loopcf]; dg += -alphaFixed[loopcf] * termFixed[loopcf]; dg2 += alphaFixed[loopcf] * alphaFixed[loopcf] * termFixed[loopcf]; } double g2 = g * g; double g3 = g * g2; double g4 = g * g3; // double dx2 = -((df2 * g - f * dg2) / g2 - (df * g - f * dg) * 2 * dg / g3); // Backward sweep double dx2Bar = 1.0; double gBar = (df2 / g2 - 2 * f * dg2 / g3 - 4 * df * dg / g3 + 6 * dg * dg * f / g4) * dx2Bar; double dgBar = (2 * df / g2 - 4 * f * dg / g3) * dx2Bar; double dg2Bar = f / g2 * dx2Bar; double fBar = (dg2 / g2 - 2 * dg * dg / g3) * dx2Bar; double dfBar = 2 * dg / g2 * dx2Bar; double df2Bar = -1.0 / g * dx2Bar; final double[] alphaFixedBar = new double[nbDcff]; final double[] termFixedBar = new double[nbDcff]; for (int loopcf = 0; loopcf < nbDcff; loopcf++) { termFixedBar[loopcf] = gBar - alphaFixed[loopcf] * dgBar + alphaFixed[loopcf] * alphaFixed[loopcf] * dg2Bar; alphaFixedBar[loopcf] = termFixed[loopcf] * (-x - alphaFixed[loopcf]) * termFixedBar[loopcf] - termFixed[loopcf] * dgBar + 2 * alphaFixed[loopcf] * termFixed[loopcf] * dg2Bar; } final double[] alphaIborBar = new double[nbDcfi]; final double[] termIborBar = new double[nbDcfi]; for (int loopcf = 0; loopcf < nbDcfi; loopcf++) { termIborBar[loopcf] = fBar - alphaIbor[loopcf] * dfBar + alphaIbor[loopcf] * alphaIbor[loopcf] * df2Bar; alphaIborBar[loopcf] = termIbor[loopcf] * (-x - alphaIbor[loopcf]) * termIborBar[loopcf] - termIbor[loopcf] * dfBar + 2 * alphaIbor[loopcf] * termIbor[loopcf] * df2Bar; } return ObjectsPair.of(alphaFixedBar, alphaIborBar); } }