/// <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)); }
//------------------------------------------------------------------------- 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))); }