SwaptionPhysicalFixedIborLMMDDMethod { public MultipleCurrencyAmount presentValue(final SwaptionPhysicalFixedIbor swaption, final LiborMarketModelDisplacedDiffusionProviderInterface lmmData) { ArgumentChecker.notNull(swaption, "Swaption"); ArgumentChecker.notNull(lmmData, "LMM provider"); final Currency ccy = swaption.getCurrency(); final MulticurveProviderInterface multicurves = lmmData.getMulticurveProvider(); final LiborMarketModelDisplacedDiffusionParameters parameters = lmmData.getLMMParameters(); final AnnuityPaymentFixed cfe = swaption.getUnderlyingSwap().accept(CFEC, multicurves); final int nbCFInit = cfe.getNumberOfPayments(); final double multFact = Math.signum(cfe.getNthPayment(0).getAmount()); final boolean isCall = (cfe.getNthPayment(0).getAmount() < 0); final double[] cftInit = new double[nbCFInit]; final double[] cfaInit = new double[nbCFInit]; for (int loopcf = 0; loopcf < nbCFInit; loopcf++) { cftInit[loopcf] = cfe.getNthPayment(loopcf).getPaymentTime(); cfaInit[loopcf] = cfe.getNthPayment(loopcf).getAmount() * -multFact; } final double timeToExpiry = swaption.getTimeToExpiry(); final int nbFactor = parameters.getNbFactor(); final double[][] volLMM = parameters.getVolatility(); final double[] timeLMM = parameters.getIborTime(); final int[] indCFDate = new int[nbCFInit]; int indStart = nbCFInit - 1; int indEnd = 0; for (int loopcf = 0; loopcf < nbCFInit; loopcf++) { indCFDate[loopcf] = Arrays.binarySearch(timeLMM, cftInit[loopcf]); if (indCFDate[loopcf] < 0) { if (timeLMM[-indCFDate[loopcf] - 1] - cftInit[loopcf] < TIME_TOLERANCE) { indCFDate[loopcf] = -indCFDate[loopcf] - 1; } else { if (cftInit[loopcf] - timeLMM[-indCFDate[loopcf] - 2] < TIME_TOLERANCE) { indCFDate[loopcf] = -indCFDate[loopcf] - 2; } else { Validate.isTrue(true, "Instrument time incompatible with LMM parametrisation"); } } } if (indCFDate[loopcf] < indStart) { indStart = indCFDate[loopcf]; } if (indCFDate[loopcf] > indEnd) { indEnd = indCFDate[loopcf]; } } final int nbCF = indEnd - indStart + 1; final double[] cfa = new double[nbCF]; for (int loopcf = 0; loopcf < nbCFInit; loopcf++) { cfa[indCFDate[loopcf] - indStart] = cfaInit[loopcf]; } final double[] cft = new double[nbCF]; System.arraycopy(timeLMM, indStart, cft, 0, nbCF); final double[] dfLMM = new double[nbCF]; for (int loopcf = 0; loopcf < nbCF; loopcf++) { dfLMM[loopcf] = multicurves.getDiscountFactor(ccy, cft[loopcf]); } final double[][] gammaLMM = new double[nbCF - 1][nbFactor]; final double[] deltaLMM = new double[nbCF - 1]; System.arraycopy(parameters.getAccrualFactor(), indStart, deltaLMM, 0, nbCF - 1); final double[] aLMM = new double[nbCF - 1]; System.arraycopy(parameters.getDisplacement(), indStart, aLMM, 0, nbCF - 1); final double[] liborLMM = new double[nbCF - 1]; final double amr = parameters.getMeanReversion(); for (int loopcf = 0; loopcf < nbCF - 1; loopcf++) { gammaLMM[loopcf] = volLMM[indStart + loopcf]; liborLMM[loopcf] = (dfLMM[loopcf] / dfLMM[loopcf + 1] - 1.0d) / deltaLMM[loopcf]; } final double[] cfaMod = new double[nbCF + 1]; final double cfaMod0 = cfa[0]; cfaMod[0] = cfaMod0; cfaMod[1] = 0.0; System.arraycopy(cfa, 1, cfaMod, 2, nbCF - 1); final double[] p0 = new double[nbCF]; final double[] dP = new double[nbCF]; double b0 = 0; for (int loopcf = 0; loopcf < nbCF; loopcf++) { p0[loopcf] = dfLMM[loopcf] / dfLMM[0]; dP[loopcf] = cfaMod[loopcf + 1] * p0[loopcf]; b0 += dP[loopcf]; } final double bK = -cfaMod0; final double bM = (b0 + bK) / 2.0d; final double meanReversionImpact = Math.abs(amr) < 1.0E-6 ? timeToExpiry : (Math.exp(2.0d * amr * timeToExpiry) - 1.0d) / (2.0d * amr); final double[] rate0Ratio = new double[nbCF - 1]; final double[][] mu0 = new double[nbCF - 1][nbFactor]; for (int loopcf = 0; loopcf < nbCF - 1; loopcf++) { rate0Ratio[loopcf] = (liborLMM[loopcf] + aLMM[loopcf]) / (liborLMM[loopcf] + 1 / deltaLMM[loopcf]); } for (int loopfact = 0; loopfact < nbFactor; loopfact++) { mu0[0][loopfact] = rate0Ratio[0] * gammaLMM[0][loopfact]; } for (int loopcf = 1; loopcf < nbCF - 1; loopcf++) { for (int loopfact = 0; loopfact < nbFactor; loopfact++) { mu0[loopcf][loopfact] = mu0[loopcf - 1][loopfact] + rate0Ratio[loopcf] * gammaLMM[loopcf][loopfact]; } } final double[] tau = new double[nbCF]; final double[] tau2 = new double[nbCF]; for (int loopcf = 0; loopcf < nbCF - 1; loopcf++) { for (int loopfact = 0; loopfact < nbFactor; loopfact++) { tau2[loopcf + 1] += mu0[loopcf][loopfact] * mu0[loopcf][loopfact]; } tau2[loopcf + 1] = tau2[loopcf + 1] * meanReversionImpact; tau[loopcf + 1] = Math.sqrt(tau2[loopcf + 1]); } double sumNum = -bM; double sumDen = 0; for (int loopcf = 0; loopcf < nbCF; loopcf++) { sumNum += dP[loopcf] - dP[loopcf] * tau2[loopcf] / 2.0; sumDen += dP[loopcf] * tau[loopcf]; } final double xBar = sumNum / sumDen; final double[] pM = new double[nbCF]; for (int loopcf = 0; loopcf < nbCF; loopcf++) { pM[loopcf] = p0[loopcf] * (1 - xBar * tau[loopcf] - tau2[loopcf] / 2.0); } final double[] liborM = new double[nbCF - 1]; final double[] alphaM = new double[nbCF]; for (int loopcf = 0; loopcf < nbCF - 1; loopcf++) { liborM[loopcf] = (pM[loopcf] / pM[loopcf + 1] - 1.0d) / deltaLMM[loopcf]; } for (int loopcf = 0; loopcf < nbCF; loopcf++) { alphaM[loopcf] = cfaMod[loopcf + 1] * pM[loopcf] / bM; } final double[] rateMRatio = new double[nbCF - 1]; final double[][] muM = new double[nbCF - 1][nbFactor]; for (int loopcf = 0; loopcf < nbCF - 1; loopcf++) { rateMRatio[loopcf] = (liborM[loopcf] + aLMM[loopcf]) / (liborM[loopcf] + 1 / deltaLMM[loopcf]); } for (int loopfact = 0; loopfact < nbFactor; loopfact++) { muM[0][loopfact] = rateMRatio[0] * gammaLMM[0][loopfact]; } for (int loopcf = 1; loopcf < nbCF - 1; loopcf++) { for (int loopfact = 0; loopfact < nbFactor; loopfact++) { muM[loopcf][loopfact] = muM[loopcf - 1][loopfact] + rateMRatio[loopcf] * gammaLMM[loopcf][loopfact]; } } double normSigmaM = 0; final double[] sigmaM = new double[nbFactor]; for (int loopfact = 0; loopfact < nbFactor; loopfact++) { for (int loopcf = 0; loopcf < nbCF - 1; loopcf++) { sigmaM[loopfact] += alphaM[loopcf + 1] * muM[loopcf][loopfact]; } normSigmaM += sigmaM[loopfact] * sigmaM[loopfact]; } final double impliedBlackVol = Math.sqrt(normSigmaM * meanReversionImpact); final EuropeanVanillaOption option = new EuropeanVanillaOption(bK, 1, isCall); final BlackPriceFunction blackFunction = new BlackPriceFunction(); final BlackFunctionData dataBlack = new BlackFunctionData(b0, 1.0, impliedBlackVol); final Function1D<BlackFunctionData, Double> func = blackFunction.getPriceFunction(option); final double pv = dfLMM[0] * func.evaluate(dataBlack); return MultipleCurrencyAmount.of(swaption.getUnderlyingSwap().getFirstLeg().getCurrency(), pv * (swaption.isLong() ? 1.0 : -1.0)); } private  SwaptionPhysicalFixedIborLMMDDMethod(); static SwaptionPhysicalFixedIborLMMDDMethod getInstance(); MultipleCurrencyAmount presentValue(final SwaptionPhysicalFixedIbor swaption, final LiborMarketModelDisplacedDiffusionProviderInterface lmmData); double[][] presentValueLMMSensitivity(final SwaptionPhysicalFixedIbor swaption, final LiborMarketModelDisplacedDiffusionProviderInterface lmmData); double[] presentValueDDSensitivity(final SwaptionPhysicalFixedIbor swaption, final LiborMarketModelDisplacedDiffusionProviderInterface lmmData); MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final SwaptionPhysicalFixedIbor swaption, //CSIGNORE
      final LiborMarketModelDisplacedDiffusionProviderInterface lmmData);  }
@Test public void presentValue() { final double pvPreviousRun = 2000842.564; final MultipleCurrencyAmount pv = METHOD_LMM.presentValue(SWAPTION_PAYER_LONG, LMM_MULTICURVES); assertEquals("Swaption physical - LMM - present value", pvPreviousRun, pv.getAmount(EUR), TOLERANCE_PV); }
@Test public void presentValueMC() { LiborMarketModelMonteCarloMethod methodLmmMc; methodLmmMc = new LiborMarketModelMonteCarloMethod(new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH); final MultipleCurrencyAmount pvMC = methodLmmMc.presentValue(SWAPTION_PAYER_LONG, EUR, LMM_MULTICURVES); final double pvMCPreviousRun = 1997241.514; assertEquals("Swaption physical - LMM - present value Monte Carlo", pvMCPreviousRun, pvMC.getAmount(EUR), TOLERANCE_PV); final MultipleCurrencyAmount pvApprox = METHOD_LMM.presentValue(SWAPTION_PAYER_LONG, LMM_MULTICURVES); final double pvbp = METHOD_SWAP.presentValueBasisPoint(SWAP_RECEIVER, MULTICURVES); final double forward = SWAP_RECEIVER.accept(PRDC, MULTICURVES); final BlackFunctionData data = new BlackFunctionData(forward, pvbp, 0.20); final EuropeanVanillaOption option = new EuropeanVanillaOption(RATE, SWAPTION_PAYER_LONG.getTimeToExpiry(), FIXED_IS_PAYER); final BlackImpliedVolatilityFormula implied = new BlackImpliedVolatilityFormula(); final double impliedVolMC = implied.getImpliedVolatility(data, option, pvMC.getAmount(EUR)); final double impliedVolApprox = implied.getImpliedVolatility(data, option, pvApprox.getAmount(EUR)); assertEquals("Swaption physical - LMM - present value Approximation/Monte Carlo", impliedVolMC, impliedVolApprox, 2.0E-3); }
@Test public void longShortParity() { final MultipleCurrencyAmount pvLong = METHOD_LMM.presentValue(SWAPTION_PAYER_LONG, LMM_MULTICURVES); final MultipleCurrencyAmount pvShort = METHOD_LMM.presentValue(SWAPTION_PAYER_SHORT, LMM_MULTICURVES); assertEquals("Swaption physical - LMM - present value - long/short parity", pvLong.getAmount(EUR), -pvShort.getAmount(EUR), TOLERANCE_PV); }
@Test public void payerReceiverParity() { final MultipleCurrencyAmount pvReceiverLong = METHOD_LMM.presentValue(SWAPTION_RECEIVER_LONG, LMM_MULTICURVES); final MultipleCurrencyAmount pvPayerShort = METHOD_LMM.presentValue(SWAPTION_PAYER_SHORT, LMM_MULTICURVES); final MultipleCurrencyAmount pvSwap = SWAP_RECEIVER.accept(PVDC, MULTICURVES); assertEquals("Swaption physical - LMM - present value - payer/receiver/swap parity", pvReceiverLong.getAmount(EUR) + pvPayerShort.getAmount(EUR), pvSwap.getAmount(EUR), TOLERANCE_PV); }
@Test public void calibrateAtBestPriceAmortized() { final double[] amortization = new double[] {1.00, 0.80, 0.60, 0.40, 0.20 }; final int nbPeriods = amortization.length; final SwapFixedIborDefinition swapDefinition = SwapFixedIborDefinition.from(SETTLEMENT_DATE, Period.ofYears(nbPeriods), EUR1YEURIBOR6M, NOTIONAL, RATE, FIXED_IS_PAYER); final CouponFixedDefinition[] cpnFixed = new CouponFixedDefinition[nbPeriods]; final AnnuityCouponFixedDefinition legFixed = swapDefinition.getFixedLeg(); final CouponIborDefinition[] cpnIbor = new CouponIborDefinition[2 * nbPeriods]; final AnnuityDefinition<? extends PaymentDefinition> legIbor = swapDefinition.getSecondLeg(); for (int loopexp = 0; loopexp < nbPeriods; loopexp++) { cpnFixed[loopexp] = legFixed.getNthPayment(loopexp).withNotional(legFixed.getNthPayment(loopexp).getNotional() * amortization[loopexp]); cpnIbor[2 * loopexp] = ((CouponIborDefinition) legIbor.getNthPayment(2 * loopexp)) .withNotional(((CouponIborDefinition) legIbor.getNthPayment(2 * loopexp)).getNotional() * amortization[loopexp]); cpnIbor[2 * loopexp + 1] = ((CouponIborDefinition) legIbor.getNthPayment(2 * loopexp + 1)).withNotional(((CouponIborDefinition) legIbor.getNthPayment(2 * loopexp + 1)).getNotional() * amortization[loopexp]); } final SwapFixedIborDefinition swapAmortizedDefinition = new SwapFixedIborDefinition(new AnnuityCouponFixedDefinition(cpnFixed, TARGET), new AnnuityCouponIborDefinition(cpnIbor, EURIBOR6M, TARGET)); final SwaptionPhysicalFixedIborDefinition swaptionAmortizedDefinition = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, swapAmortizedDefinition, FIXED_IS_PAYER, IS_LONG); final SwaptionPhysicalFixedIbor swaptionAmortized = swaptionAmortizedDefinition.toDerivative(REFERENCE_DATE); final PresentValueSABRSensitivityDataBundle pvss = METHOD_SABR_LMM_ATBEST.presentValueSABRSensitivity(swaptionAmortized, SABR_MULTICURVES); final double[] shift = new double[] {0.0001, 0.0001, 0.0001 }; final double[] toleranceSABRSensi = new double[] {5.0E+4, 5.0E+3, 1.0E+4 }; final double[] sensiComputed = new double[] {pvss.getAlpha().toSingleValue(), pvss.getRho().toSingleValue(), pvss.getNu().toSingleValue() }; final double[] sensiExpected = new double[shift.length]; SABRInterestRateParameters sabrParameterShift; SABRSwaptionProvider sabrBundleShift; for (int loopp = 0; loopp < shift.length; loopp++) { sabrParameterShift = SABRDataSets.createSABR1ParameterBumped(shift[loopp], loopp); sabrBundleShift = new SABRSwaptionProvider(MULTICURVES, sabrParameterShift, EUR1YEURIBOR6M); final MultipleCurrencyAmount pvShiftPlus = METHOD_SABR_LMM_ATBEST.presentValue(swaptionAmortized, sabrBundleShift); sabrParameterShift = SABRDataSets.createSABR1ParameterBumped(-shift[loopp], loopp); sabrBundleShift = new SABRSwaptionProvider(MULTICURVES, sabrParameterShift, EUR1YEURIBOR6M); final MultipleCurrencyAmount pvShiftMinus = METHOD_SABR_LMM_ATBEST.presentValue(swaptionAmortized, sabrBundleShift); sensiExpected[loopp] = (pvShiftPlus.getAmount(EUR) - pvShiftMinus.getAmount(EUR)) / (2 * shift[loopp]); assertEquals("SwaptionPhysicalFixedIborLMM: Calibration at best - SABR sensitivity " + loopp, sensiExpected[loopp], sensiComputed[loopp], toleranceSABRSensi[loopp]); } }