@Override public Double evaluate(final Double x) { return presentValueDeltaStandard( _forward, x, _expiry, _capStandard.isCap(), _df, _capStandard.getNotional(), _capStandard.getPaymentYearFraction()); }
public InArrearsDeltaIntegrant( final CapFloorIbor capStandard, final MulticurveProviderInterface curves) { _capStandard = capStandard; _forward = curves.getSimplyCompoundForwardRate( capStandard.getIndex(), capStandard.getFixingPeriodStartTime(), capStandard.getFixingPeriodEndTime(), capStandard.getFixingAccrualFactor()); _expiry = capStandard.getFixingTime(); _df = curves.getDiscountFactor(capStandard.getCurrency(), capStandard.getPaymentTime()); }
@Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueCurveSensitivityMethodVsCalculator() { final SABRInterestRateDataBundle sabrExtraBundle = new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES); final InterestRateCurveSensitivity pvsMethod = METHOD.presentValueSensitivity(CAP_HIGH_LONG, SABR_BUNDLE); final InterestRateCurveSensitivity pvsCalculator = new InterestRateCurveSensitivity(CAP_HIGH_LONG.accept(PVSC, sabrExtraBundle)); assertEquals( "Cap/floor: SABR with extrapolation pv curve sensitivity - Method vs Calculator", pvsMethod, pvsCalculator); }
@Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueMethodVsCalculator() { final SABRInterestRateDataBundle sabrExtraBundle = new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES); final CurrencyAmount pvMethod = METHOD.presentValue(CAP_LONG, SABR_BUNDLE); final PresentValueSABRExtrapolationCalculator pvc = new PresentValueSABRExtrapolationCalculator(CUT_OFF_STRIKE, MU); final double pvCalculator = CAP_LONG.accept(pvc, sabrExtraBundle); assertEquals( "Cap/floor: SABR with extrapolation pricing - Method vs Calculator", pvMethod.getAmount(), pvCalculator, 1E-2); }
@Test /** * Test the present value SABR parameters sensitivity against a finite difference computation; * strike above the cut-off strike. */ public void testPresentValueSABRSensitivityAboveCutOff() { final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1(); final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1(); final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves); final CurrencyAmount pv = METHOD.presentValue(CAP_HIGH_LONG, sabrBundle); final PresentValueSABRSensitivityDataBundle pvsCapLong = METHOD.presentValueSABRSensitivity(CAP_HIGH_LONG, sabrBundle); PresentValueSABRSensitivityDataBundle pvsCapShort = METHOD.presentValueSABRSensitivity(CAP_HIGH_SHORT, sabrBundle); // Long/short parity pvsCapShort = pvsCapShort.multiplyBy(-1.0); assertEquals(pvsCapShort.getAlpha(), pvsCapLong.getAlpha()); // SABR sensitivity vs finite difference final double shift = 0.0001; final double shiftAlpha = 0.00001; final DoublesPair expectedExpiryTenor = new DoublesPair( CAP_HIGH_LONG.getFixingTime(), CAP_HIGH_LONG.getFixingPeriodEndTime() - CAP_HIGH_LONG.getFixingPeriodStartTime()); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterAlphaBumped = TestsDataSetsSABR.createSABR1AlphaBumped(shiftAlpha); final SABRInterestRateDataBundle sabrBundleAlphaBumped = new SABRInterestRateDataBundle(sabrParameterAlphaBumped, curves); final CurrencyAmount pvLongPayerAlphaBumped = METHOD.presentValue(CAP_HIGH_LONG, sabrBundleAlphaBumped); final double expectedAlphaSensi = (pvLongPayerAlphaBumped.getAmount() - pv.getAmount()) / shiftAlpha; assertEquals("Number of alpha sensitivity", pvsCapLong.getAlpha().getMap().keySet().size(), 1); assertEquals( "Alpha sensitivity expiry/tenor", pvsCapLong.getAlpha().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Alpha sensitivity value", expectedAlphaSensi, pvsCapLong.getAlpha().getMap().get(expectedExpiryTenor), 1.0E-0); // Rho sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterRhoBumped = TestsDataSetsSABR.createSABR1RhoBumped(); final SABRInterestRateDataBundle sabrBundleRhoBumped = new SABRInterestRateDataBundle(sabrParameterRhoBumped, curves); final CurrencyAmount pvLongPayerRhoBumped = METHOD.presentValue(CAP_HIGH_LONG, sabrBundleRhoBumped); final double expectedRhoSensi = (pvLongPayerRhoBumped.getAmount() - pv.getAmount()) / shift; assertEquals("Number of rho sensitivity", pvsCapLong.getRho().getMap().keySet().size(), 1); assertEquals( "Rho sensitivity expiry/tenor", pvsCapLong.getRho().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Rho sensitivity value", pvsCapLong.getRho().getMap().get(expectedExpiryTenor), expectedRhoSensi, 1.0E-1); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterNuBumped = TestsDataSetsSABR.createSABR1NuBumped(); final SABRInterestRateDataBundle sabrBundleNuBumped = new SABRInterestRateDataBundle(sabrParameterNuBumped, curves); final CurrencyAmount pvLongPayerNuBumped = METHOD.presentValue(CAP_HIGH_LONG, sabrBundleNuBumped); final double expectedNuSensi = (pvLongPayerNuBumped.getAmount() - pv.getAmount()) / shift; assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1); assertEquals( "Nu sensitivity expiry/tenor", pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Nu sensitivity value", pvsCapLong.getNu().getMap().get(expectedExpiryTenor), expectedNuSensi, 2.0E-1); }
@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); } }
@Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueAboveCutOff() { CurrencyAmount methodPrice = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE); final double df = CURVES.getCurve(FUNDING_CURVE_NAME).getDiscountFactor(CAP_HIGH_LONG.getPaymentTime()); final double forward = CAP_HIGH_LONG.accept(PRC, CURVES); final double maturity = CAP_HIGH_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime(); final DoublesPair expiryMaturity = new DoublesPair(CAP_HIGH_LONG.getFixingTime(), maturity); final double alpha = SABR_PARAMETERS.getAlpha(expiryMaturity); final double beta = SABR_PARAMETERS.getBeta(expiryMaturity); final double rho = SABR_PARAMETERS.getRho(expiryMaturity); final double nu = SABR_PARAMETERS.getNu(expiryMaturity); final SABRFormulaData sabrParam = new SABRFormulaData(alpha, beta, rho, nu); final SABRExtrapolationRightFunction sabrExtrapolation = new SABRExtrapolationRightFunction( forward, sabrParam, CUT_OFF_STRIKE, CAP_HIGH_LONG.getFixingTime(), MU); final EuropeanVanillaOption option = new EuropeanVanillaOption( CAP_HIGH_LONG.getStrike(), CAP_HIGH_LONG.getFixingTime(), CAP_HIGH_LONG.isCap()); final double expectedPrice = sabrExtrapolation.price(option) * CAP_HIGH_LONG.getNotional() * CAP_HIGH_LONG.getPaymentYearFraction() * df; assertEquals( "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2); methodPrice = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE); assertEquals( "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2); }
@Override public Double visitCapFloorIbor(final CapFloorIbor payment, final YieldCurveBundle data) { return visitCouponIborSpread(payment.toCoupon(), data); }
/** * Computes the present value of an Ibor cap/floor in arrears by replication based on the paper, * "Swap and Cap/Floors with Fixing in Arrears or Payment Delay," OpenGamma Quantitative * Documentation * http://developers.opengamma.com/quantitative-research/In-Arrears-and-Payment-Delay-Swaps-and-Caps-OpenGamma.pdf * * @param cap The cap/floor * @param curves The curves * @return The present value */ public MultipleCurrencyAmount presentValue( final CapFloorIbor cap, final MulticurveProviderInterface curves) { ArgumentChecker.notNull(cap, "The cap/floor shoud not be null"); ArgumentChecker.notNull(curves, "curves"); final Currency ccy = cap.getCurrency(); // Construct a "standard" CapFloorIbor whose paymentTime is set to be fixingPeriodEndTime CapFloorIbor capStandard = new CapFloorIbor( cap.getCurrency(), cap.getFixingPeriodEndTime(), cap.getPaymentYearFraction(), cap.getNotional(), cap.getFixingTime(), cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor(), cap.getStrike(), cap.isCap()); final double forward = curves.getSimplyCompoundForwardRate( cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor()); final double beta = (1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); final double df = curves.getDiscountFactor(capStandard.getCurrency(), capStandard.getPaymentTime()); final double strikePart = (1.0 + cap.getFixingAccrualFactor() * capStandard.getStrike()) * presentValueStandard( forward, capStandard.getStrike(), capStandard.getFixingTime(), capStandard.isCap(), df, capStandard.getNotional(), capStandard.getPaymentYearFraction()); final InArrearsIntegrant integrant = new InArrearsIntegrant(capStandard, curves); double integralPart; try { if (cap.isCap()) { double atmVol = _smileFunction.getVolatility(forward); double upper = forward * Math.exp(6.0 * atmVol * Math.sqrt(cap.getFixingTime())); double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, strike, upper); double reminder = integrant.evaluate(upper) * upper; double error = reminder / integralPart; int count = 0; while (Math.abs(error) > REL_ERROR && count < MAX_COUNT) { integralPart += INTEGRATOR.integrate(integrant, upper, 2.0 * upper); upper *= 2.0; // The increase of integralPart in the next loop is bounded by reminder reminder = integrant.evaluate(upper) * upper; error = reminder / integralPart; ++count; if (count == MAX_COUNT) { LOGGER.info( "Maximum iteration count, " + MAX_COUNT + ", has been reached. Relative error is greater than " + REL_ERROR); } } } else { double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, REL_TOL * strike, strike); } } catch (final Exception e) { throw new MathException(e); } integralPart *= 2.0 * cap.getFixingAccrualFactor(); final double pv = (strikePart + integralPart) / beta; return MultipleCurrencyAmount.of(cap.getCurrency(), pv); }
/** * Computes the pv sensitivity of an Ibor cap/floor in arrears * * @param cap The cap/floor * @param curves The curves * @return The sensitivity */ public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity( final CapFloorIbor cap, final MulticurveProviderInterface curves) { ArgumentChecker.notNull(cap, "The cap/floor shoud not be null"); ArgumentChecker.notNull(curves, "curves"); final Currency ccy = cap.getCurrency(); // Construct a "standard" CapFloorIbor whose paymentTime is set to be fixingPeriodEndTime CapFloorIbor capStandard = new CapFloorIbor( cap.getCurrency(), cap.getFixingPeriodEndTime(), cap.getPaymentYearFraction(), cap.getNotional(), cap.getFixingTime(), cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor(), cap.getStrike(), cap.isCap()); final double forward = curves.getSimplyCompoundForwardRate( cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor()); final double beta = (1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); double df = curves.getDiscountFactor(capStandard.getCurrency(), capStandard.getPaymentTime()); double strikePart = (1.0 + cap.getFixingAccrualFactor() * capStandard.getStrike()) * presentValueStandard( forward, capStandard.getStrike(), capStandard.getFixingTime(), capStandard.isCap(), df, capStandard.getNotional(), capStandard.getPaymentYearFraction()); double strikePartDelta = (1.0 + cap.getFixingAccrualFactor() * capStandard.getStrike()) * presentValueDeltaStandard( forward, capStandard.getStrike(), capStandard.getFixingTime(), capStandard.isCap(), df, capStandard.getNotional(), capStandard.getPaymentYearFraction()); final InArrearsIntegrant integrant = new InArrearsIntegrant(capStandard, curves); double integralPart; double upper = 0.0; try { if (cap.isCap()) { double atmVol = _smileFunction.getVolatility(forward); upper = forward * Math.exp(6.0 * atmVol * Math.sqrt(cap.getFixingTime())); double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, strike, upper); double reminder = integrant.evaluate(upper) * upper; double error = reminder / integralPart; int count = 0; while (Math.abs(error) > REL_ERROR && count < MAX_COUNT) { integralPart += INTEGRATOR.integrate(integrant, upper, 2.0 * upper); upper *= 2.0; // The increase of integralPart in the next loop is bounded by reminder reminder = integrant.evaluate(upper) * upper; error = reminder / integralPart; ++count; if (count == MAX_COUNT) { LOGGER.info( "Maximum iteration count, " + MAX_COUNT + ", has been reached. Relative error is greater than " + REL_ERROR); } } } else { double strike = cap.getStrike(); integralPart = INTEGRATOR.integrate(integrant, REL_TOL * strike, strike); } } catch (final Exception e) { throw new MathException(e); } integralPart *= 2.0 * cap.getFixingAccrualFactor(); double pv = (strikePart + integralPart) / beta; double betaFwd = cap.getFixingAccrualFactor() * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); double betaDscStart = (1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) * cap.getFixingPeriodStartTime() / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); double betaDscEnd = -(1.0 + cap.getFixingAccrualFactor() * forward) * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) * cap.getFixingPeriodEndTime() / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()); List<DoublesPair> listDiscounting = new ArrayList<>(); double strikePartDsc = -capStandard.getPaymentTime() * strikePart; double integralPartDsc = -capStandard.getPaymentTime() * integralPart; listDiscounting.add( DoublesPair.of(capStandard.getPaymentTime(), (strikePartDsc + integralPartDsc) / beta)); listDiscounting.add(DoublesPair.of(cap.getFixingPeriodStartTime(), -pv * betaDscStart / beta)); listDiscounting.add(DoublesPair.of(cap.getFixingPeriodEndTime(), -pv * betaDscEnd / beta)); Map<String, List<DoublesPair>> mapDsc = new HashMap<>(); mapDsc.put(curves.getName(capStandard.getCurrency()), listDiscounting); final List<ForwardSensitivity> listForward = new ArrayList<>(); double strikePartFwd = strikePartDelta; double integralPartFwd = 0.0; final InArrearsDeltaIntegrant integrantFwd = new InArrearsDeltaIntegrant(capStandard, curves); try { if (cap.isCap()) { double strike = cap.getStrike(); integralPartFwd = INTEGRATOR.integrate(integrantFwd, strike, upper); } else { double strike = cap.getStrike(); integralPartFwd = INTEGRATOR.integrate(integrantFwd, REL_TOL * strike, strike); } } catch (final Exception e) { throw new MathException(e); } integralPartFwd *= 2.0 * cap.getFixingAccrualFactor(); listForward.add( new SimplyCompoundedForwardSensitivity( capStandard.getFixingPeriodStartTime(), capStandard.getFixingPeriodEndTime(), capStandard.getFixingAccrualFactor(), (strikePartFwd + integralPartFwd - pv * betaFwd) / beta)); Map<String, List<ForwardSensitivity>> mapFwd = new HashMap<>(); mapFwd.put(curves.getName(capStandard.getIndex()), listForward); return MultipleCurrencyMulticurveSensitivity.of( cap.getCurrency(), MulticurveSensitivity.of(mapDsc, mapFwd)); }
@Override public DecisionSchedule visitCapFloorIbor( final CapFloorIbor payment, final MulticurveProviderInterface multicurves) { final double[] decisionTime = new double[] {payment.getFixingTime()}; final double fixingStartTime = payment.getFixingPeriodStartTime(); final double fixingEndTime = payment.getFixingPeriodEndTime(); final double paymentTime = payment.getPaymentTime(); final double[][] impactTime = new double[1][]; impactTime[0] = new double[] {fixingStartTime, fixingEndTime, paymentTime}; final double[][] impactAmount = new double[1][]; double forward = multicurves.getForwardRate( payment.getIndex(), payment.getFixingPeriodStartTime(), payment.getFixingPeriodEndTime(), payment.getFixingAccrualFactor()); final double beta = (1.0 + payment.getFixingAccrualFactor() * forward) * multicurves.getDiscountFactor(payment.getCurrency(), payment.getFixingPeriodEndTime()) / multicurves.getDiscountFactor( payment.getCurrency(), payment.getFixingPeriodStartTime()); impactAmount[0] = new double[] {beta, -1.0, 1.0}; final DecisionSchedule decision = new DecisionSchedule(decisionTime, impactTime, impactAmount); return decision; }