/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.model; import static com.opengamma.strata.collect.TestHelper.assertSerialization; import static com.opengamma.strata.collect.TestHelper.coverImmutableBean; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import java.time.LocalDate; import org.testng.annotations.Test; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.basics.index.IborIndex; import com.opengamma.strata.basics.index.IborIndices; import com.opengamma.strata.basics.value.ValueDerivatives; import com.opengamma.strata.collect.DoubleArrayMath; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.collect.array.DoubleMatrix; import com.opengamma.strata.collect.tuple.Pair; import com.opengamma.strata.pricer.impl.rate.model.HullWhiteOneFactorPiecewiseConstantInterestRateModel; import com.opengamma.strata.pricer.model.HullWhiteOneFactorPiecewiseConstantParameters; /** * Test {@link HullWhiteOneFactorPiecewiseConstantInterestRateModel}. */ @Test public class HullWhiteOneFactorPiecewiseConstantInterestRateModelTest { private static final ReferenceData REF_DATA = ReferenceData.standard(); private static final double MEAN_REVERSION = 0.01; private static final DoubleArray VOLATILITY = DoubleArray.of(0.01, 0.011, 0.012, 0.013, 0.014); private static final DoubleArray VOLATILITY_TIME = DoubleArray.of(0.5, 1.0, 2.0, 5.0); private static final HullWhiteOneFactorPiecewiseConstantParameters MODEL_PARAMETERS = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, VOLATILITY, VOLATILITY_TIME); private static final HullWhiteOneFactorPiecewiseConstantInterestRateModel MODEL = HullWhiteOneFactorPiecewiseConstantInterestRateModel.DEFAULT; private static final DoubleArray DCF_FIXED = DoubleArray.of(0.50, 0.48); private static final DoubleArray ALPHA_FIXED = DoubleArray.of(0.02, 0.04); private static final DoubleArray DCF_IBOR = DoubleArray.of(-1.0, -0.01, 0.01, -0.01, 0.95); private static final DoubleArray ALPHA_IBOR = DoubleArray.of(0.00, 0.01, 0.02, 0.03, 0.04); private static final double TOLERANCE_RATE = 1.0E-10; private static final double TOLERANCE_RATE_DELTA = 1.0E-8; private static final double TOLERANCE_RATE_DELTA2 = 1.0E-7; private static final double TOLERANCE_ALPHA = 1E-8; private static final IborIndex EURIBOR3M = IborIndices.EUR_EURIBOR_3M; /** * Tests the class getters. */ public void getter() { assertEquals(MEAN_REVERSION, MODEL_PARAMETERS.getMeanReversion()); for (int loopperiod = 0; loopperiod < VOLATILITY.size(); loopperiod++) { assertEquals(VOLATILITY.get(loopperiod), MODEL_PARAMETERS.getVolatility().get(loopperiod)); } double[] volTime = MODEL_PARAMETERS.getVolatilityTime().toArray(); for (int loopperiod = 0; loopperiod < VOLATILITY_TIME.size(); loopperiod++) { assertEquals(VOLATILITY_TIME.get(loopperiod), volTime[loopperiod + 1]); } } /** * Tests the class setters. */ public void setter() { double volReplaced = 0.02; HullWhiteOneFactorPiecewiseConstantParameters param1 = MODEL_PARAMETERS.withLastVolatility(volReplaced); assertEquals(volReplaced, param1.getVolatility().get(param1.getVolatility().size() - 1)); HullWhiteOneFactorPiecewiseConstantParameters param2 = MODEL_PARAMETERS.withLastVolatility(VOLATILITY.get(VOLATILITY.size() - 1)); for (int loopperiod = 0; loopperiod < param2.getVolatility().size(); loopperiod++) { assertEquals(VOLATILITY.get(loopperiod), param2.getVolatility().get(loopperiod)); } } /** * Tests the equal and hash code methods. */ public void equalHash() { HullWhiteOneFactorPiecewiseConstantParameters newParameter = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, VOLATILITY, VOLATILITY_TIME); assertTrue(MODEL_PARAMETERS.equals(newParameter)); assertTrue(MODEL_PARAMETERS.hashCode() == newParameter.hashCode()); HullWhiteOneFactorPiecewiseConstantParameters modifiedParameter = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION + 0.01, VOLATILITY, VOLATILITY_TIME); assertFalse(MODEL_PARAMETERS.equals(modifiedParameter)); } /** * Test the future convexity adjustment factor v a hard-coded value. */ public 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.getVolatility().size(); ValueDerivatives factorDeriv = MODEL.futuresConvexityFactorAdjoint(MODEL_PARAMETERS, tradeLastTime, fixStartTime, fixEndTime); double factor2 = factorDeriv.getValue(); double[] sigmaBar = factorDeriv.getDerivatives().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); } } /** * Test the payment delay convexity adjustment factor. */ public 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); } /** * Test the bond volatility (called alpha) vs a hard-coded value. */ public void alpha() { double expiry1 = 0.25; double expiry2 = 2.25; double numeraire = 10.0; double maturity = 9.0; double alphaExpected = -0.015191631; double alpha = MODEL.alpha(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity); //All data assertEquals(alphaExpected, alpha, TOLERANCE_ALPHA); alphaExpected = -0.015859116; alpha = MODEL.alpha(MODEL_PARAMETERS, 0.0, expiry2, numeraire, maturity);//From today assertEquals(alphaExpected, alpha, TOLERANCE_ALPHA); alphaExpected = 0.111299267; alpha = MODEL.alpha(MODEL_PARAMETERS, 0.0, expiry2, expiry2, maturity);// From today with expiry numeraire assertEquals(alphaExpected, alpha, TOLERANCE_ALPHA); alpha = MODEL.alpha(MODEL_PARAMETERS, 0.0, 0.0, numeraire, maturity); // From 0 to 0 assertEquals(0.0d, alpha, TOLERANCE_ALPHA); } /** * Test the adjoint algorithmic differentiation version of alpha. */ public 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.getValue(); double[] alphaDerivatives = alphaDeriv.getDerivatives().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]; System.arraycopy(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); } } /** * Test the swaption exercise boundary. */ public void kappa() { double[] cashFlowAmount = new double[] {-1.0, 0.05, 0.05, 0.05, 0.05, 1.05 }; double notional = 100000000; // 100m double[] cashFlowTime = new double[] {10.0, 11.0, 12.0, 13.0, 14.00, 15.00 }; double expiryTime = cashFlowTime[0] - 2.0 / 365.0; int nbCF = cashFlowAmount.length; double[] discountedCashFlow = new double[nbCF]; double[] alpha = new double[nbCF]; double rate = 0.04; for (int loopcf = 0; loopcf < nbCF; loopcf++) { discountedCashFlow[loopcf] = cashFlowAmount[loopcf] * Math.exp(-rate * cashFlowTime[loopcf]) * notional; alpha[loopcf] = MODEL.alpha(MODEL_PARAMETERS, 0.0, expiryTime, expiryTime, cashFlowTime[loopcf]); } double kappa = MODEL.kappa(DoubleArray.copyOf(discountedCashFlow), DoubleArray.copyOf(alpha)); double swapValue = 0.0; for (int loopcf = 0; loopcf < nbCF; loopcf++) { swapValue += discountedCashFlow[loopcf] * Math.exp(-Math.pow(alpha[loopcf], 2.0) / 2.0 - alpha[loopcf] * kappa); } assertEquals(0.0, swapValue, 1.0E-1); } public void swapRate() { double shift = 1.0E-4; double x = 0.1; double numerator = 0.0; for (int loopcf = 0; loopcf < DCF_IBOR.size(); loopcf++) { numerator += DCF_IBOR.get(loopcf) * Math.exp(-ALPHA_IBOR.get(loopcf) * x - 0.5 * ALPHA_IBOR.get(loopcf) * ALPHA_IBOR.get(loopcf)); } double denominator = 0.0; for (int loopcf = 0; loopcf < DCF_FIXED.size(); loopcf++) { denominator += DCF_FIXED.get(loopcf) * Math.exp(-ALPHA_FIXED.get(loopcf) * x - 0.5 * ALPHA_FIXED.get(loopcf) * ALPHA_FIXED.get(loopcf)); } double swapRateExpected = -numerator / denominator; double swapRateComputed = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); assertEquals(swapRateExpected, swapRateComputed, TOLERANCE_RATE); double swapRatePlus = MODEL.swapRate(x + shift, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double swapRateMinus = MODEL.swapRate(x - shift, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double swapRateDx1Expected = (swapRatePlus - swapRateMinus) / (2 * shift); double swapRateDx1Computed = MODEL.swapRateDx1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); assertEquals(swapRateDx1Expected, swapRateDx1Computed, TOLERANCE_RATE_DELTA); double swapRateDx2Expected = (swapRatePlus + swapRateMinus - 2 * swapRateComputed) / (shift * shift); double swapRateDx2Computed = MODEL.swapRateDx2(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); assertEquals(swapRateDx2Expected, swapRateDx2Computed, TOLERANCE_RATE_DELTA2); } public void swapRateDdcf() { double shift = 1.0E-8; double x = 0.0; ValueDerivatives computed = MODEL.swapRateDdcff1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double swapRateComputed = computed.getValue(); double[] ddcffComputed = computed.getDerivatives().toArray(); double swapRateExpected = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); assertEquals(swapRateComputed, swapRateExpected, TOLERANCE_RATE); double[] ddcffExpected = new double[DCF_FIXED.size()]; for (int loopcf = 0; loopcf < DCF_FIXED.size(); loopcf++) { double[] dsf_bumped = DCF_FIXED.toArray(); dsf_bumped[loopcf] += shift; double swapRatePlus = MODEL.swapRate(x, DoubleArray.copyOf(dsf_bumped), ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); dsf_bumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRate(x, DoubleArray.copyOf(dsf_bumped), ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); ddcffExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } assertTrue(DoubleArrayMath.fuzzyEquals(ddcffExpected, ddcffComputed, TOLERANCE_RATE_DELTA)); double[] ddcfiExpected = new double[DCF_IBOR.size()]; for (int loopcf = 0; loopcf < DCF_IBOR.size(); loopcf++) { double[] dsf_bumped = DCF_IBOR.toArray(); dsf_bumped[loopcf] += shift; double swapRatePlus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DoubleArray.copyOf(dsf_bumped), ALPHA_IBOR); dsf_bumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DoubleArray.copyOf(dsf_bumped), ALPHA_IBOR); ddcfiExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } double[] ddcfiComputed = MODEL.swapRateDdcfi1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR) .getDerivatives().toArray(); assertTrue(DoubleArrayMath.fuzzyEquals(ddcfiExpected, ddcfiComputed, TOLERANCE_RATE_DELTA)); } public void swapRateDa() { double shift = 1.0E-8; double x = 0.0; ValueDerivatives computed = MODEL.swapRateDaf1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double swapRateComputed = computed.getValue(); double[] dafComputed = computed.getDerivatives().toArray(); double swapRateExpected = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); assertEquals(swapRateComputed, swapRateExpected, TOLERANCE_RATE); double[] dafExpected = new double[ALPHA_FIXED.size()]; for (int loopcf = 0; loopcf < ALPHA_FIXED.size(); loopcf++) { double[] afBumped = ALPHA_FIXED.toArray(); afBumped[loopcf] += shift; double swapRatePlus = MODEL.swapRate(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR); afBumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRate(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR); dafExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } assertTrue(DoubleArrayMath.fuzzyEquals(dafExpected, dafComputed, TOLERANCE_RATE_DELTA)); double[] daiExpected = new double[DCF_IBOR.size()]; for (int loopcf = 0; loopcf < DCF_IBOR.size(); loopcf++) { double[] aiBumped = ALPHA_IBOR.toArray(); aiBumped[loopcf] += shift; double swapRatePlus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped)); aiBumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped)); daiExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } double[] daiComputed = MODEL.swapRateDai1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR).getDerivatives().toArray(); assertTrue(DoubleArrayMath.fuzzyEquals(daiExpected, daiComputed, TOLERANCE_RATE_DELTA)); } public void swapRateDx2Ddcf() { double shift = 1.0E-7; double x = 0.0; Pair<DoubleArray, DoubleArray> dx2ddcfComputed = MODEL.swapRateDx2Ddcf1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double[] dx2DdcffExpected = new double[DCF_FIXED.size()]; for (int loopcf = 0; loopcf < DCF_FIXED.size(); loopcf++) { double[] dsf_bumped = DCF_FIXED.toArray(); dsf_bumped[loopcf] += shift; double swapRatePlus = MODEL.swapRateDx2(x, DoubleArray.copyOf(dsf_bumped), ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); dsf_bumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRateDx2(x, DoubleArray.copyOf(dsf_bumped), ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); dx2DdcffExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } assertTrue(DoubleArrayMath.fuzzyEquals(dx2DdcffExpected, dx2ddcfComputed.getFirst().toArray(), TOLERANCE_RATE_DELTA2)); double[] dx2DdcfiExpected = new double[DCF_IBOR.size()]; for (int loopcf = 0; loopcf < DCF_IBOR.size(); loopcf++) { double[] dsf_bumped = DCF_IBOR.toArray(); dsf_bumped[loopcf] += shift; double swapRatePlus = MODEL.swapRateDx2(x, DCF_FIXED, ALPHA_FIXED, DoubleArray.copyOf(dsf_bumped), ALPHA_IBOR); dsf_bumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRateDx2(x, DCF_FIXED, ALPHA_FIXED, DoubleArray.copyOf(dsf_bumped), ALPHA_IBOR); dx2DdcfiExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } assertTrue(DoubleArrayMath.fuzzyEquals(dx2DdcfiExpected, dx2ddcfComputed.getSecond().toArray(), TOLERANCE_RATE_DELTA2)); } public void swapRateDx2Da() { double shift = 1.0E-7; double x = 0.0; Pair<DoubleArray, DoubleArray> dx2DaComputed = MODEL.swapRateDx2Da1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR); double[] dx2DafExpected = new double[DCF_FIXED.size()]; for (int loopcf = 0; loopcf < DCF_FIXED.size(); loopcf++) { double[] afBumped = ALPHA_FIXED.toArray(); afBumped[loopcf] += shift; double swapRatePlus = MODEL.swapRateDx2(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR); afBumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRateDx2(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR); dx2DafExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } assertTrue(DoubleArrayMath.fuzzyEquals(dx2DafExpected, dx2DaComputed.getFirst().toArray(), TOLERANCE_RATE_DELTA2)); double[] dx2DaiExpected = new double[DCF_IBOR.size()]; for (int loopcf = 0; loopcf < DCF_IBOR.size(); loopcf++) { double[] aiBumped = ALPHA_IBOR.toArray(); aiBumped[loopcf] += shift; double swapRatePlus = MODEL.swapRateDx2(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped)); aiBumped[loopcf] -= 2 * shift; double swapRateMinus = MODEL.swapRateDx2(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped)); dx2DaiExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift); } assertTrue(DoubleArrayMath.fuzzyEquals(dx2DaiExpected, dx2DaComputed.getSecond().toArray(), TOLERANCE_RATE_DELTA2)); } //------------------------------------------------------------------------- // Here methods used for Bermudan swaption pricing and Monte-Carlo are test weakly by regression to 2.x. // Proper tests should be added when these pricing methodologies are available. public void test_beta() { double[] theta = new double[] {0.0, 0.9930234298974474, 1.5013698630136987, 1.9917808219178081, 2.5013698630136987, 2.9972602739726026, 3.5013698630136987, 3.9972602739726026, 4.501220151208923, 4.998487910771765, 5.495890410958904 }; double[] expected = new double[] {0.010526360888642377, 0.008653752074472373, 0.008551601997542554, 0.009479708049949437, 0.009409731278859806, 0.009534948404597303, 0.009504300650429525, 0.009629338816014276, 0.009613195012744198, 0.010403528524805543 }; for (int i = 0; i < theta.length - 1; ++i) { assertEquals(MODEL.beta(MODEL_PARAMETERS, theta[i], theta[i + 1]), expected[i], TOLERANCE_RATE); } } public void test_lambda() { DoubleArray cashFlow = DoubleArray.of(1.1342484780379178E8, 178826.75595605336, -1.1353458434950349E8); DoubleArray alphaSq = DoubleArray.of(0.0059638289722142215, 0.0069253776359785415, 0.007985436623619701); DoubleArray hwH = DoubleArray.of(5.357967757629822, 5.593630711441366, 5.828706853806842); double computed = MODEL.lambda(cashFlow, alphaSq, hwH); assertEquals(computed, -0.0034407112369635212, TOLERANCE_RATE); double value = 0.0; for (int loopcf = 0; loopcf < 3; loopcf++) { value += cashFlow.get(loopcf) * Math.exp(-0.5 * alphaSq.get(loopcf) - hwH.get(loopcf) * computed); } assertEquals(value, 0d, 1.0E-7); } public void test_volatilityMaturityPart() { double u = 5.001332435062505; DoubleMatrix v = DoubleMatrix.copyOf(new double[][] {{5.012261396811139, 5.515068493150685, 6.010958904109589, 6.515068493150685, 7.010958904109589, 7.515068493150685, 8.01095890410959, 8.520458118122614, 9.017725877685455, 9.515068493150684, 10.013698630136986 } }); DoubleMatrix computed = MODEL.volatilityMaturityPart(MODEL_PARAMETERS, u, v); double[] expected = new double[] {0.010395243419747402, 0.48742124221025085, 0.9555417903726049, 1.4290478001940943, 1.8925104710768026, 2.361305017379811, 2.8201561576361778, 3.289235677728508, 3.7447552766260217, 4.198083407732067, 4.650327387669373 }; assertTrue(DoubleArrayMath.fuzzyEquals(computed.row(0).toArray(), expected, TOLERANCE_RATE)); } //------------------------------------------------------------------------- public void coverage() { coverImmutableBean(MODEL); } public void test_serialization() { assertSerialization(MODEL); } //------------------------------------------------------------------------- /** * Tests of performance. "enabled = false" for the standard testing. */ @Test(enabled = false) public void performanceAlphaAdjoint() { double expiry1 = 0.25; double expiry2 = 2.25; double numeraire = 10.0; double maturity = 9.0; int nbVolatility = VOLATILITY.size(); long startTime, endTime; int nbTest = 100000; double alpha = 0.0; startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { alpha = MODEL.alpha(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity); } endTime = System.currentTimeMillis(); System.out.println(nbTest + " alpha Hull-White: " + (endTime - startTime) + " ms"); startTime = System.currentTimeMillis(); for (int looptest = 0; looptest < nbTest; looptest++) { ValueDerivatives computed = MODEL.alphaAdjoint(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity); alpha = computed.getValue(); } endTime = System.currentTimeMillis(); System.out.println(nbTest + " alpha Hull-White adjoint (value+" + nbVolatility + " derivatives): " + (endTime - startTime) + " ms"); // Performance note: value: 31-Aug-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 75 ms for 1000000 swaptions. // Performance note: value+derivatives: 31-Aug-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 100 ms for 1000000 swaptions. System.out.println("Alpha: " + alpha); } /** * Test the payment delay convexity adjustment factor. Analysis of the size. * In normal test, should have (enabled=false) */ @Test(enabled = false) public void paymentDelayConvexityFactorAnalysis() { double hwMeanReversion = 0.01; double rate = 0.02; double[] tenorTime = {0.25, 0.50 }; int nbTenors = tenorTime.length; double[] lagPayTime = {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)); double[][][] factor = new double[nbTenors][nbLags][nbPeriods]; double[][][] adj = new double[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); } } } @SuppressWarnings("unused") int t = 0; t++; } }