public InterestRateCurveSensitivity presentValueSensitivity( final BondIborTransaction bond, final YieldCurveBundle curves) { final InterestRateCurveSensitivity pvsNominal = new InterestRateCurveSensitivity( bond.getBondTransaction().getNominal().accept(PVSC, curves)); final InterestRateCurveSensitivity pvsCoupon = new InterestRateCurveSensitivity( bond.getBondTransaction().getCoupon().accept(PVSC, curves)); final double settlementAmount = bond.getTransactionPrice() * bond.getBondTransaction() .getCoupon() .getNthPayment(0) .getNotional(); // FIXME: add accrued. LOGGER.error("The FRN settlement amount does not include the accrued interests."); final PaymentFixed settlement = new PaymentFixed( bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount, bond.getBondTransaction().getRepoCurveName()); final InterestRateCurveSensitivity pvsSettlement = new InterestRateCurveSensitivity(settlement.accept(PVSC, curves)); return pvsNominal.plus(pvsCoupon).multipliedBy(bond.getQuantity()).plus(pvsSettlement); }
/** * Computes the option security price curve sensitivity. The future price is computed without * convexity adjustment. * * @param security The future option security. * @param sabrData The SABR data bundle. * @return The security price curve sensitivity. */ public InterestRateCurveSensitivity priceCurveSensitivity( final InterestRateFutureOptionMarginSecurity security, final SABRInterestRateDataBundle sabrData) { // Forward sweep final double priceFuture = METHOD_FUTURE.price(security.getUnderlyingFuture(), sabrData); final double rateStrike = 1.0 - security.getStrike(); final EuropeanVanillaOption option = new EuropeanVanillaOption(rateStrike, security.getExpirationTime(), !security.isCall()); final double forward = 1 - priceFuture; final double delay = security.getUnderlyingFuture().getLastTradingTime() - security.getExpirationTime(); final double[] volatilityAdjoint = sabrData .getSABRParameter() .getVolatilityAdjoint(security.getExpirationTime(), delay, rateStrike, forward); final BlackFunctionData dataBlack = new BlackFunctionData(forward, 1.0, volatilityAdjoint[0]); final double[] priceAdjoint = BLACK_FUNCTION.getPriceAdjoint(option, dataBlack); // Backward sweep final double priceBar = 1.0; final double volatilityBar = priceAdjoint[2] * priceBar; final double forwardBar = priceAdjoint[1] * priceBar + volatilityAdjoint[1] * volatilityBar; final double priceFutureBar = -forwardBar; final InterestRateCurveSensitivity priceFutureDerivative = METHOD_FUTURE.priceCurveSensitivity(security.getUnderlyingFuture(), sabrData); return priceFutureDerivative.multipliedBy(priceFutureBar); }
/** * Computes the present value curve sensitivity of a transaction. * * @param transaction The future option transaction. * @param curves The yield curve bundle. * @return The present value curve sensitivity. */ public InterestRateCurveSensitivity presentValueCurveSensitivity( final InterestRateFutureOptionMarginTransaction transaction, final YieldCurveBundle curves) { InterestRateCurveSensitivity securitySensitivity = _securityMethod.priceCurveSensitivity(transaction.getUnderlyingOption(), curves); return securitySensitivity.multipliedBy( transaction.getQuantity() * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional() * transaction.getUnderlyingOption().getUnderlyingFuture().getPaymentAccrualFactor()); }
/** * Computes the present value curve sensitivity of a transaction. * * @param transaction The future option transaction. * @param curves The yield curve bundle. * @return The present value curve sensitivity. */ public InterestRateCurveSensitivity presentValueCurveSensitivity( final BondFutureOptionPremiumTransaction transaction, final YieldCurveBundle curves) { ArgumentChecker.notNull(transaction, "transaction"); ArgumentChecker.notNull(curves, "curves"); final InterestRateCurveSensitivity premiumSensitivity = PVCSC.visit(transaction.getPremium(), curves); final InterestRateCurveSensitivity securitySensitivity = METHOD_SECURITY.priceCurveSensitivity(transaction.getUnderlyingOption(), curves); return premiumSensitivity.plus( securitySensitivity.multipliedBy( transaction.getQuantity() * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional())); }
@Test public void presentValueCurveSensitivity() { final InterestRateCurveSensitivity pvcsComputed = METHOD_SECURITY.presentValueCurveSensitivity(BILL_IAM_SEC, CURVE_BUNDLE); assertEquals( "Bill Security: present value curve sensitivity", 1, pvcsComputed.getSensitivities().size()); assertEquals( "Bill Security: present value curve sensitivity", 1, pvcsComputed.getSensitivities().get(NAME_CURVES[1]).size()); final double deltaTolerancePrice = 1.0E+2; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 0.01 unit for a 1 bp move. final double deltaShift = 1.0E-6; // Credit curve sensitivity final String bumpedCurveName = "Bumped Curve"; final BillSecurity billBumped = BILL_IAM_SEC_DEFINITION.toDerivative(REFERENCE_DATE, NAME_CURVES[0], bumpedCurveName); final double[] nodeTimes = new double[] {billBumped.getEndTime()}; final double[] sensi = SensitivityFiniteDifference.curveSensitivity( billBumped, CURVE_BUNDLE, NAME_CURVES[1], bumpedCurveName, nodeTimes, deltaShift, METHOD_SECURITY); final List<DoublesPair> sensiPv = pvcsComputed.getSensitivities().get(NAME_CURVES[1]); for (int loopnode = 0; loopnode < sensi.length; loopnode++) { final DoublesPair pairPv = sensiPv.get(loopnode); assertEquals( "Bill Security: curve sensitivity - Node " + loopnode, nodeTimes[loopnode], pairPv.getFirst(), 1E-8); AssertJUnit.assertEquals( "Bill Security: curve sensitivity", pairPv.second, sensi[loopnode], deltaTolerancePrice); } }
/** * Compute the present value sensitivity of a bond transaction. * * @param bond The bond transaction. * @param curves The curve bundle. * @return The present value sensitivity. */ public InterestRateCurveSensitivity presentValueSensitivity( final BondFixedTransaction bond, final YieldCurveBundle curves) { final InterestRateCurveSensitivity pvsNominal = new InterestRateCurveSensitivity( bond.getBondTransaction().getNominal().accept(PVSC, curves)); final InterestRateCurveSensitivity pvsCoupon = new InterestRateCurveSensitivity( bond.getBondTransaction().getCoupon().accept(PVSC, curves)); final double settlementAmount = -(bond.getTransactionPrice() * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional() + bond.getBondTransaction().getAccruedInterest()) * bond.getQuantity(); final PaymentFixed settlement = new PaymentFixed( bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount, bond.getBondTransaction().getRepoCurveName()); final InterestRateCurveSensitivity pvsSettlement = new InterestRateCurveSensitivity(settlement.accept(PVSC, curves)); return pvsNominal.plus(pvsCoupon).multipliedBy(bond.getQuantity()).plus(pvsSettlement); }
@Test /** Tests the curve sensitivity. */ public void presentValueCurveSensitivity() { InterestRateCurveSensitivity pvsSwaption = METHOD_HW_APPROXIMATION.presentValueCurveSensitivity(SWAPTION_PAYER_LONG, BUNDLE_HW); pvsSwaption = pvsSwaption.cleaned(); final double deltaTolerancePrice = 1.0E+4; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. Tolerance // increased to cope with numerical imprecision of finite difference. final double deltaShift = 1.0E-6; // 1. Forward curve sensitivity final String bumpedCurveName = "Bumped Curve"; final SwaptionCashFixedIbor swptBumpedForward = SWAPTION_PAYER_LONG_DEFINITION.toDerivative( REFERENCE_DATE, new String[] {CURVES_NAME[0], bumpedCurveName}); DoubleAVLTreeSet forwardTime = new DoubleAVLTreeSet(); for (int loopcpn = 0; loopcpn < SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNumberOfPayments(); loopcpn++) { CouponIbor cpn = (CouponIbor) SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNthPayment(loopcpn); forwardTime.add(cpn.getFixingPeriodStartTime()); forwardTime.add(cpn.getFixingPeriodEndTime()); } double[] nodeTimesForward = forwardTime.toDoubleArray(); final double[] sensiForwardMethod = SensitivityFiniteDifference.curveSensitivity( swptBumpedForward, BUNDLE_HW, CURVES_NAME[1], bumpedCurveName, nodeTimesForward, deltaShift, METHOD_HW_APPROXIMATION); final List<DoublesPair> sensiPvForward = pvsSwaption.getSensitivities().get(CURVES_NAME[1]); for (int loopnode = 0; loopnode < sensiForwardMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvForward.get(loopnode); assertEquals( "Sensitivity swaption pv to forward curve: Node " + loopnode, nodeTimesForward[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity " + loopnode, sensiForwardMethod[loopnode], pairPv.second, deltaTolerancePrice); } // 2. Discounting curve sensitivity final SwaptionCashFixedIbor swptBumpedDisc = SWAPTION_PAYER_LONG_DEFINITION.toDerivative( REFERENCE_DATE, new String[] {bumpedCurveName, CURVES_NAME[1]}); DoubleAVLTreeSet discTime = new DoubleAVLTreeSet(); discTime.add(SWAPTION_PAYER_LONG.getSettlementTime()); for (int loopcpn = 0; loopcpn < SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNumberOfPayments(); loopcpn++) { CouponIbor cpn = (CouponIbor) SWAPTION_PAYER_LONG.getUnderlyingSwap().getSecondLeg().getNthPayment(loopcpn); discTime.add(cpn.getPaymentTime()); } double[] nodeTimesDisc = discTime.toDoubleArray(); final double[] sensiDiscMethod = SensitivityFiniteDifference.curveSensitivity( swptBumpedDisc, BUNDLE_HW, CURVES_NAME[0], bumpedCurveName, nodeTimesDisc, deltaShift, METHOD_HW_APPROXIMATION); assertEquals( "Sensitivity finite difference method: number of node", 11, sensiDiscMethod.length); final List<DoublesPair> sensiPvDisc = pvsSwaption.getSensitivities().get(CURVES_NAME[0]); for (int loopnode = 0; loopnode < sensiDiscMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvDisc.get(loopnode); assertEquals( "Sensitivity swaption pv to forward curve: Node " + loopnode, nodeTimesDisc[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity", sensiDiscMethod[loopnode], pairPv.second, deltaTolerancePrice); } }
/** * The method calibrates a LMM on a set of vanilla swaption priced with SABR. The set of vanilla * swaptions is given by the CalibrationType. The curve and SABR sensitivities of the original * swaption are calculated with LMM re-calibration. Used mainly for performance test purposes as * the output is hybrid list. * * @param swaption The swaption. * @param curves The curves and SABR data. * @return The results (returned as a list of objects) [0] the present value, [1] the present * curve sensitivity, [2] the present value SABR sensitivity. */ public List<Object> presentValueCurveSABRSensitivity( final SwaptionPhysicalFixedIbor swaption, final SABRInterestRateDataBundle curves) { ArgumentChecker.notNull(swaption, "swaption"); ArgumentChecker.notNull(curves, "curves"); // TODO: Create a way to chose the LMM base parameters (displacement, mean reversion, // volatility). final LiborMarketModelDisplacedDiffusionParameters lmmParameters = LiborMarketModelDisplacedDiffusionParameters.from( swaption, DEFAULT_DISPLACEMENT, DEFAULT_MEAN_REVERSION, new VolatilityLMMAngle(DEFAULT_ANGLE, DEFAULT_DISPLACEMENT)); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective objective = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective(lmmParameters); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine calibrationEngine = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine(objective); final SwaptionPhysicalFixedIbor[] swaptionCalibration = METHOD_BASKET.calibrationBasketFixedLegPeriod(swaption); calibrationEngine.addInstrument(swaptionCalibration, METHOD_SWAPTION_SABR); calibrationEngine.calibrate(curves); final LiborMarketModelDisplacedDiffusionDataBundle lmmBundle = new LiborMarketModelDisplacedDiffusionDataBundle(lmmParameters, curves); // Risks final int nbCal = swaptionCalibration.length; final int nbFact = lmmParameters.getNbFactor(); final List<Integer> instrumentIndex = calibrationEngine.getInstrumentIndex(); final double[] dPvAmdLambda = new double[nbCal]; final double[][][] dPvCaldGamma = new double[nbCal][][]; final double[][] dPvCaldLambda = new double[nbCal][nbCal]; final PresentValueSABRSensitivityDataBundle[] dPvCaldSABR = new PresentValueSABRSensitivityDataBundle[nbCal]; InterestRateCurveSensitivity pvcsCal = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaption, lmmBundle); pvcsCal = pvcsCal.cleaned(); final double[][] dPvAmdGamma = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaption, lmmBundle); for (int loopcal = 0; loopcal < nbCal; loopcal++) { dPvCaldGamma[loopcal] = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaptionCalibration[loopcal], lmmBundle); } // Multiplicative-factor sensitivity for (int loopcal = 0; loopcal < nbCal; loopcal++) { for (int loopperiod = instrumentIndex.get(loopcal); loopperiod < instrumentIndex.get(loopcal + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvAmdLambda[loopcal] += dPvAmdGamma[loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { for (int loopcal2 = 0; loopcal2 < nbCal; loopcal2++) { for (int loopperiod = instrumentIndex.get(loopcal2); loopperiod < instrumentIndex.get(loopcal2 + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvCaldLambda[loopcal1][loopcal2] += dPvCaldGamma[loopcal1][loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } } final InterestRateCurveSensitivity[] pvcsCalBase = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalCal = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalDiff = new InterestRateCurveSensitivity[nbCal]; for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcsCalBase[loopcal] = METHOD_SWAPTION_SABR.presentValueCurveSensitivity(swaptionCalibration[loopcal], curves); pvcsCalBase[loopcal] = pvcsCalBase[loopcal].cleaned(); pvcsCalCal[loopcal] = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaptionCalibration[loopcal], lmmBundle); pvcsCalCal[loopcal] = pvcsCalCal[loopcal].cleaned(); pvcsCalDiff[loopcal] = pvcsCalBase[loopcal].plus(pvcsCalCal[loopcal].multipliedBy(-1)); pvcsCalDiff[loopcal] = pvcsCalDiff[loopcal].cleaned(); } final CommonsMatrixAlgebra matrix = new CommonsMatrixAlgebra(); final DoubleMatrix2D dPvCaldLambdaMatrix = new DoubleMatrix2D(dPvCaldLambda); final DoubleMatrix2D dPvCaldLambdaMatrixInverse = matrix.getInverse(dPvCaldLambdaMatrix); // SABR sensitivity final double[][] dPvCaldAlpha = new double[nbCal][nbCal]; final double[][] dPvCaldRho = new double[nbCal][nbCal]; final double[][] dPvCaldNu = new double[nbCal][nbCal]; for (int loopcal = 0; loopcal < nbCal; loopcal++) { dPvCaldSABR[loopcal] = METHOD_SWAPTION_SABR.presentValueSABRSensitivity(swaptionCalibration[loopcal], curves); final Set<DoublesPair> keySet = dPvCaldSABR[loopcal].getAlpha().getMap().keySet(); final DoublesPair[] keys = keySet.toArray(new DoublesPair[keySet.size()]); dPvCaldAlpha[loopcal][loopcal] = dPvCaldSABR[loopcal].getAlpha().getMap().get(keys[0]); dPvCaldRho[loopcal][loopcal] = dPvCaldSABR[loopcal].getRho().getMap().get(keys[0]); dPvCaldNu[loopcal][loopcal] = dPvCaldSABR[loopcal].getNu().getMap().get(keys[0]); } final DoubleMatrix1D dPvAmdLambdaMatrix = new DoubleMatrix1D(dPvAmdLambda); final DoubleMatrix2D dPvCaldAlphaMatrix = new DoubleMatrix2D(dPvCaldAlpha); final DoubleMatrix2D dLambdadAlphaMatrix = (DoubleMatrix2D) matrix.multiply(dPvCaldLambdaMatrixInverse, dPvCaldAlphaMatrix); final DoubleMatrix2D dPvAmdAlphaMatrix = (DoubleMatrix2D) matrix.multiply(matrix.getTranspose(dLambdadAlphaMatrix), dPvAmdLambdaMatrix); final DoubleMatrix2D dPvCaldRhoMatrix = new DoubleMatrix2D(dPvCaldRho); final DoubleMatrix2D dLambdadRhoMatrix = (DoubleMatrix2D) matrix.multiply(dPvCaldLambdaMatrixInverse, dPvCaldRhoMatrix); final DoubleMatrix2D dPvAmdRhoMatrix = (DoubleMatrix2D) matrix.multiply(matrix.getTranspose(dLambdadRhoMatrix), dPvAmdLambdaMatrix); final DoubleMatrix2D dPvCaldNuMatrix = new DoubleMatrix2D(dPvCaldNu); final DoubleMatrix2D dLambdadNuMatrix = (DoubleMatrix2D) matrix.multiply(dPvCaldLambdaMatrixInverse, dPvCaldNuMatrix); final DoubleMatrix2D dPvAmdNuMatrix = (DoubleMatrix2D) matrix.multiply(matrix.getTranspose(dLambdadNuMatrix), dPvAmdLambdaMatrix); final double[] dPvAmdAlpha = matrix.getTranspose(dPvAmdAlphaMatrix).getData()[0]; final double[] dPvAmdRho = matrix.getTranspose(dPvAmdRhoMatrix).getData()[0]; final double[] dPvAmdNu = matrix.getTranspose(dPvAmdNuMatrix).getData()[0]; // Storage in PresentValueSABRSensitivityDataBundle final PresentValueSABRSensitivityDataBundle pvss = new PresentValueSABRSensitivityDataBundle(); for (int loopcal = 0; loopcal < nbCal; loopcal++) { final DoublesPair expiryMaturity = DoublesPair.of( swaptionCalibration[loopcal].getTimeToExpiry(), swaptionCalibration[loopcal].getMaturityTime()); pvss.addAlpha(expiryMaturity, dPvAmdAlpha[loopcal]); pvss.addRho(expiryMaturity, dPvAmdRho[loopcal]); pvss.addNu(expiryMaturity, dPvAmdNu[loopcal]); } // Curve sensitivity final InterestRateCurveSensitivity[] dLambdadC = new InterestRateCurveSensitivity[nbCal]; for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { dLambdadC[loopcal1] = new InterestRateCurveSensitivity(); for (int loopcal2 = 0; loopcal2 <= loopcal1; loopcal2++) { dLambdadC[loopcal1] = dLambdadC[loopcal1].plus( pvcsCalDiff[loopcal2].multipliedBy( dPvCaldLambdaMatrixInverse.getEntry(loopcal1, loopcal2))); } } InterestRateCurveSensitivity pvcs = new InterestRateCurveSensitivity(); for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcs = pvcs.plus(dLambdadC[loopcal].multipliedBy(dPvAmdLambda[loopcal])); } pvcs = pvcs.plus(pvcsCal); pvcs = pvcs.cleaned(); final List<Object> results = new ArrayList<>(); results.add( CurrencyAmount.of( swaption.getCurrency(), METHOD_SWAPTION_LMM.presentValue(swaption, lmmBundle).getAmount())); results.add(pvcs); results.add(pvss); return results; }
/** * The method calibrates a LMM on a set of vanilla swaption priced with SABR. The set of vanilla * swaptions is given by the CalibrationType. The curve sensitivities of the original swaption are * calculated with LMM re-calibration. * * @param swaption The swaption. * @param curves The curves and SABR data. * @return The present value curve sensitivities. */ public InterestRateCurveSensitivity presentValueCurveSensitivity( final SwaptionPhysicalFixedIbor swaption, final SABRInterestRateDataBundle curves) { ArgumentChecker.notNull(swaption, "swaption"); ArgumentChecker.notNull(curves, "curves"); // TODO: Create a way to chose the LMM base parameters (displacement, mean reversion, // volatility). final LiborMarketModelDisplacedDiffusionParameters lmmParameters = LiborMarketModelDisplacedDiffusionParameters.from( swaption, DEFAULT_DISPLACEMENT, DEFAULT_MEAN_REVERSION, new VolatilityLMMAngle(DEFAULT_ANGLE, DEFAULT_DISPLACEMENT)); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective objective = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationObjective(lmmParameters); final SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine calibrationEngine = new SwaptionPhysicalLMMDDSuccessiveRootFinderCalibrationEngine(objective); final SwaptionPhysicalFixedIbor[] swaptionCalibration = METHOD_BASKET.calibrationBasketFixedLegPeriod(swaption); calibrationEngine.addInstrument(swaptionCalibration, METHOD_SWAPTION_SABR); calibrationEngine.calibrate(curves); final LiborMarketModelDisplacedDiffusionDataBundle lmmBundle = new LiborMarketModelDisplacedDiffusionDataBundle(lmmParameters, curves); // Risks final int nbCal = swaptionCalibration.length; final int nbFact = lmmParameters.getNbFactor(); final List<Integer> instrumentIndex = calibrationEngine.getInstrumentIndex(); final double[] dPvAmdLambda = new double[nbCal]; final double[][][] dPvCaldGamma = new double[nbCal][][]; final double[][] dPvCaldLambda = new double[nbCal][nbCal]; InterestRateCurveSensitivity pvcsCal = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaption, lmmBundle); pvcsCal = pvcsCal.cleaned(); final double[][] dPvAmdGamma = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaption, lmmBundle); for (int loopcal = 0; loopcal < nbCal; loopcal++) { dPvCaldGamma[loopcal] = METHOD_SWAPTION_LMM.presentValueLMMSensitivity(swaptionCalibration[loopcal], lmmBundle); } // Multiplicative-factor sensitivity for (int loopcal = 0; loopcal < nbCal; loopcal++) { for (int loopperiod = instrumentIndex.get(loopcal); loopperiod < instrumentIndex.get(loopcal + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvAmdLambda[loopcal] += dPvAmdGamma[loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { for (int loopcal2 = 0; loopcal2 < nbCal; loopcal2++) { for (int loopperiod = instrumentIndex.get(loopcal2); loopperiod < instrumentIndex.get(loopcal2 + 1); loopperiod++) { for (int loopfact = 0; loopfact < nbFact; loopfact++) { dPvCaldLambda[loopcal1][loopcal2] += dPvCaldGamma[loopcal1][loopperiod][loopfact] * lmmParameters.getVolatility()[loopperiod][loopfact]; } } } } final InterestRateCurveSensitivity[] pvcsCalBase = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalCal = new InterestRateCurveSensitivity[nbCal]; final InterestRateCurveSensitivity[] pvcsCalDiff = new InterestRateCurveSensitivity[nbCal]; for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcsCalBase[loopcal] = METHOD_SWAPTION_SABR.presentValueCurveSensitivity(swaptionCalibration[loopcal], curves); pvcsCalBase[loopcal] = pvcsCalBase[loopcal].cleaned(); pvcsCalCal[loopcal] = METHOD_SWAPTION_LMM.presentValueCurveSensitivity(swaptionCalibration[loopcal], lmmBundle); pvcsCalCal[loopcal] = pvcsCalCal[loopcal].cleaned(); pvcsCalDiff[loopcal] = pvcsCalBase[loopcal].plus(pvcsCalCal[loopcal].multipliedBy(-1)); pvcsCalDiff[loopcal] = pvcsCalDiff[loopcal].cleaned(); } final CommonsMatrixAlgebra matrix = new CommonsMatrixAlgebra(); final DoubleMatrix2D dPvCaldLambdaMatrix = new DoubleMatrix2D(dPvCaldLambda); final DoubleMatrix2D dPvCaldLambdaMatrixInverse = matrix.getInverse(dPvCaldLambdaMatrix); // Curve sensitivity final InterestRateCurveSensitivity[] dLambdadC = new InterestRateCurveSensitivity[nbCal]; for (int loopcal1 = 0; loopcal1 < nbCal; loopcal1++) { dLambdadC[loopcal1] = new InterestRateCurveSensitivity(); for (int loopcal2 = 0; loopcal2 <= loopcal1; loopcal2++) { dLambdadC[loopcal1] = dLambdadC[loopcal1].plus( pvcsCalDiff[loopcal2].multipliedBy( dPvCaldLambdaMatrixInverse.getEntry(loopcal1, loopcal2))); } } InterestRateCurveSensitivity pvcsAdjust = new InterestRateCurveSensitivity(); for (int loopcal = 0; loopcal < nbCal; loopcal++) { pvcsAdjust = pvcsAdjust.plus(dLambdadC[loopcal].multipliedBy(dPvAmdLambda[loopcal])); } pvcsAdjust = pvcsAdjust.cleaned(); InterestRateCurveSensitivity pvcsTot = pvcsCal.plus(pvcsAdjust); pvcsTot = pvcsTot.cleaned(); return pvcsTot; }
@Test /** * Test the present value rate sensitivity against a finite difference computation; strike above * the cut-off strike. Test sensitivity long/short parity. */ public void testPresentValueSensitivityAboveCutOff() { final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1(); final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1(); final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves); InterestRateCurveSensitivity pvsCapLong = METHOD.presentValueSensitivity(CAP_HIGH_LONG, sabrBundle); final InterestRateCurveSensitivity pvsCapShort = METHOD.presentValueSensitivity(CAP_HIGH_SHORT, sabrBundle); // Long/short parity final InterestRateCurveSensitivity pvsCapShort_1 = pvsCapShort.multipliedBy(-1); assertEquals(pvsCapLong.getSensitivities(), pvsCapShort_1.getSensitivities()); // Present value sensitivity comparison with finite difference. final double deltaTolerancePrice = 1.0E-1; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. final double deltaShift = 1.0E-7; pvsCapLong = pvsCapLong.cleaned(); final String bumpedCurveName = "Bumped Curve"; // 1. Forward curve sensitivity final String[] CurveNameBumpedForward = {FUNDING_CURVE_NAME, bumpedCurveName}; final CapFloorIbor capBumpedForward = (CapFloorIbor) CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedForward); final double[] nodeTimesForward = new double[] { capBumpedForward.getFixingPeriodStartTime(), capBumpedForward.getFixingPeriodEndTime() }; final double[] sensiForwardMethod = SensitivityFiniteDifference.curveSensitivity( capBumpedForward, SABR_BUNDLE, FORWARD_CURVE_NAME, bumpedCurveName, nodeTimesForward, deltaShift, METHOD); assertEquals( "Sensitivity finite difference method: number of node", 2, sensiForwardMethod.length); final List<DoublesPair> sensiPvForward = pvsCapLong.getSensitivities().get(FORWARD_CURVE_NAME); for (int loopnode = 0; loopnode < sensiForwardMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvForward.get(loopnode); assertEquals( "Sensitivity cap/floor pv to forward curve: Node " + loopnode, nodeTimesForward[loopnode], pairPv.getFirst(), 1E-8); // assertEquals("Sensitivity finite difference method: node sensitivity: Node " + // loopnode, pairPv.second, sensiForwardMethod[loopnode], deltaTolerancePrice); } // 2. Discounting curve sensitivity final String[] CurveNameBumpedDisc = {bumpedCurveName, FORWARD_CURVE_NAME}; final CapFloorIbor capBumpedDisc = (CapFloorIbor) CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedDisc); final double[] nodeTimesDisc = new double[] {capBumpedDisc.getPaymentTime()}; final double[] sensiDiscMethod = SensitivityFiniteDifference.curveSensitivity( capBumpedDisc, SABR_BUNDLE, FUNDING_CURVE_NAME, bumpedCurveName, nodeTimesDisc, deltaShift, METHOD); assertEquals("Sensitivity finite difference method: number of node", 1, sensiDiscMethod.length); final List<DoublesPair> sensiPvDisc = pvsCapLong.getSensitivities().get(FUNDING_CURVE_NAME); for (int loopnode = 0; loopnode < sensiDiscMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvDisc.get(loopnode); assertEquals( "Sensitivity cap/floor pv to forward curve: Node " + loopnode, nodeTimesDisc[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity", pairPv.second, sensiDiscMethod[loopnode], deltaTolerancePrice); } }
/** * Computes the present value sensitivity to the yield curves of a CMS cap/floor by replication in * the SABR framework with extrapolation on the right. * * @param cmsCapFloor The CMS cap/floor. * @param sabrData The SABR data bundle. The SABR function need to be the Hagan function. * @return The present value sensitivity to curves. */ @Override public InterestRateCurveSensitivity presentValueCurveSensitivity( final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle sabrData) { Validate.notNull(cmsCapFloor); Validate.notNull(sabrData); final SABRInterestRateParameters sabrParameter = sabrData.getSABRParameter(); final SwapFixedCoupon<? extends Payment> underlyingSwap = cmsCapFloor.getUnderlyingSwap(); final double forward = underlyingSwap.accept(PRC, sabrData); final double discountFactor = sabrData .getCurve(underlyingSwap.getFixedLeg().getNthPayment(0).getFundingCurveName()) .getDiscountFactor(cmsCapFloor.getPaymentTime()); final double strike = cmsCapFloor.getStrike(); final double maturity = underlyingSwap .getFixedLeg() .getNthPayment(underlyingSwap.getFixedLeg().getNumberOfPayments() - 1) .getPaymentTime() - cmsCapFloor.getSettlementTime(); final DoublesPair expiryMaturity = new DoublesPair(cmsCapFloor.getFixingTime(), maturity); final double alpha = sabrParameter.getAlpha(expiryMaturity); final double beta = sabrParameter.getBeta(expiryMaturity); final double rho = sabrParameter.getRho(expiryMaturity); final double nu = sabrParameter.getNu(expiryMaturity); final SABRFormulaData sabrPoint = new SABRFormulaData(alpha, beta, rho, nu); // Common final CMSIntegrant integrantPrice = new CMSIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu); final CMSDeltaIntegrant integrantDelta = new CMSDeltaIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu); final double factor = discountFactor / integrantDelta.h(forward) * integrantDelta.g(forward); final double absoluteTolerance = 1.0 / (factor * Math.abs(cmsCapFloor.getNotional()) * cmsCapFloor.getPaymentYearFraction()); final double relativeTolerance = 1E-10; final RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration()); // Price final double[] bs = integrantDelta.bsbsp(strike); @SuppressWarnings("synthetic-access") final double[] n = integrantDelta.nnp(forward); final double strikePartPrice = discountFactor * integrantDelta.k(strike) * n[0] * bs[0]; double integralPartPrice; try { if (cmsCapFloor.isCap()) { integralPartPrice = discountFactor * integrator.integrate(integrantPrice, strike, strike + getIntegrationInterval()); } else { integralPartPrice = discountFactor * integrator.integrate(integrantPrice, 0.0, strike); } } catch (final Exception e) { throw new RuntimeException(e); } final double price = (strikePartPrice + integralPartPrice) * cmsCapFloor.getNotional() * cmsCapFloor.getPaymentYearFraction(); // Delta final double strikePart = discountFactor * integrantDelta.k(strike) * (n[1] * bs[0] + n[0] * bs[1]); double integralPart; try { if (cmsCapFloor.isCap()) { integralPart = discountFactor * integrator.integrate(integrantDelta, strike, strike + getIntegrationInterval()); } else { integralPart = discountFactor * integrator.integrate(integrantDelta, 0.0, strike); } } catch (final Exception e) { throw new RuntimeException(e); } final double deltaS0 = (strikePart + integralPart) * cmsCapFloor.getNotional() * cmsCapFloor.getPaymentYearFraction(); final double deltaPD = price / discountFactor; final double sensiDF = -cmsCapFloor.getPaymentTime() * discountFactor * deltaPD; final List<DoublesPair> list = new ArrayList<>(); list.add(new DoublesPair(cmsCapFloor.getPaymentTime(), sensiDF)); final Map<String, List<DoublesPair>> resultMap = new HashMap<>(); resultMap.put( cmsCapFloor.getUnderlyingSwap().getFixedLeg().getNthPayment(0).getFundingCurveName(), list); InterestRateCurveSensitivity result = new InterestRateCurveSensitivity(resultMap); final InterestRateCurveSensitivity forwardDr = new InterestRateCurveSensitivity(cmsCapFloor.getUnderlyingSwap().accept(PRSC, sabrData)); result = result.plus(forwardDr.multipliedBy(deltaS0)); return result; }