/** * Calculates the PV01 of a swaption using the Black formula with no volatility modeling * assumptions. The implied volatility is read directly from the market data system. */ public class ConstantBlackDiscountingPV01SwaptionFunction extends ConstantBlackDiscountingSwaptionFunction { /** The PV01 calculator */ private static final InstrumentDerivativeVisitor< BlackSwaptionFlatProviderInterface, ReferenceAmount<Pair<String, Currency>>> CALCULATOR = new PV01CurveParametersCalculator<>( PresentValueCurveSensitivityBlackSwaptionCalculator.getInstance()); /** Sets the value requirement to {@link ValueRequirementNames#PV01} */ public ConstantBlackDiscountingPV01SwaptionFunction() { super(PV01); } @Override public CompiledFunctionDefinition compile( final FunctionCompilationContext context, final Instant atInstant) { return new BlackDiscountingCompiledFunction( getTargetToDefinitionConverter(context), getDefinitionToDerivativeConverter(context), true) { @Override protected Set<ComputedValue> getValues( final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues, final InstrumentDerivative derivative, final FXMatrix fxMatrix) { final BlackSwaptionFlatProvider blackData = getSwaptionBlackSurface(executionContext, inputs, target, fxMatrix); final ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues); final String desiredCurveName = desiredValue.getConstraint(CURVE); final ValueProperties properties = desiredValue.getConstraints(); final ReferenceAmount<Pair<String, Currency>> pv01 = derivative.accept(CALCULATOR, blackData); final Set<ComputedValue> results = new HashSet<>(); boolean curveNameFound = false; for (final Map.Entry<Pair<String, Currency>, Double> entry : pv01.getMap().entrySet()) { final String curveName = entry.getKey().getFirst(); if (desiredCurveName.equals(curveName)) { curveNameFound = true; } final ValueProperties curveSpecificProperties = properties.copy().withoutAny(CURVE).with(CURVE, curveName).get(); final ValueSpecification spec = new ValueSpecification(PV01, target.toSpecification(), curveSpecificProperties); results.add(new ComputedValue(spec, entry.getValue())); } if (!curveNameFound) { throw new OpenGammaRuntimeException( "Could not get sensitivities to " + desiredCurveName + " for " + target.getName()); } return results; } @Override protected Collection<ValueProperties.Builder> getResultProperties( final FunctionCompilationContext compilationContext, final ComputationTarget target) { final Collection<ValueProperties.Builder> properties = super.getResultProperties(compilationContext, target); for (ValueProperties.Builder builder : properties) { builder.withAny(CURVE); } return properties; } @Override protected boolean requirementsSet(final ValueProperties constraints) { if (super.requirementsSet(constraints)) { final Set<String> curves = constraints.getValues(CURVE); if (curves == null) { return false; } return true; } return false; } @Override public Set<ValueSpecification> getResults( final FunctionCompilationContext compilationContext, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) { Set<String> curveNames = null; for (final Map.Entry<ValueSpecification, ValueRequirement> entry : inputs.entrySet()) { final ValueSpecification key = entry.getKey(); if (key.getValueName().equals(CURVE_BUNDLE)) { curveNames = key.getProperties().getValues(CURVE); break; } } if (curveNames == null) { return null; } final Collection<ValueProperties.Builder> commonPropertiesSet = super.getResultProperties(compilationContext, target); final Set<ValueSpecification> results = Sets.newHashSetWithExpectedSize(commonPropertiesSet.size() * curveNames.size()); for (final String curveName : curveNames) { for (ValueProperties.Builder commonProperties : commonPropertiesSet) { final ValueProperties properties = commonProperties.withoutAny(CURVE).with(CURVE, curveName).get(); results.add(new ValueSpecification(PV01, target.toSpecification(), properties)); } } return results; } }; } }
public class SwaptionPhysicalFixedIborBlackMethodTest { private static final MulticurveProviderDiscount MULTICURVES = MulticurveProviderDiscountDataSets.createMulticurveEurUsd(); private static final IborIndex EURIBOR6M = MulticurveProviderDiscountDataSets.getIndexesIborMulticurveEurUsd()[1]; private static final Calendar CALENDAR = MulticurveProviderDiscountDataSets.getEURCalendar(); private static final Currency EUR = EURIBOR6M.getCurrency(); // Data private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2012, 1, 10); private static final GeneratorSwapFixedIborMaster GENERATOR_SWAP_MASTER = GeneratorSwapFixedIborMaster.getInstance(); private static final GeneratorSwapFixedIbor GENERATOR_EUR1YEURIBOR6M = GENERATOR_SWAP_MASTER.getGenerator("EUR1YEURIBOR6M", CALENDAR); private static final BlackFlatSwaptionParameters BLACK = BlackDataSets.createBlackSwaptionEUR6(); private static final BlackSwaptionFlatProviderDiscount BLACK_MULTICURVES = new BlackSwaptionFlatProviderDiscount(MULTICURVES, BLACK); // Swaption private static final Period EXPIRY_TENOR = Period.ofMonths(26); // To be between nodes. private static final ZonedDateTime EXPIRY_DATE = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, EXPIRY_TENOR, GENERATOR_EUR1YEURIBOR6M.getBusinessDayConvention(), CALENDAR, GENERATOR_EUR1YEURIBOR6M.isEndOfMonth()); private static final ZonedDateTime SETTLE_DATE = ScheduleCalculator.getAdjustedDate( EXPIRY_DATE, GENERATOR_EUR1YEURIBOR6M.getSpotLag(), CALENDAR); private static final int SWAP_TENOR_YEAR = 5; private static final Period SWAP_TENOR = Period.ofYears(SWAP_TENOR_YEAR); private static final double NOTIONAL = 123456789.0; private static final double RATE = 0.02; private static final SwapFixedIborDefinition SWAP_DEFINITION_REC = SwapFixedIborDefinition.from( SETTLE_DATE, SWAP_TENOR, GENERATOR_EUR1YEURIBOR6M, NOTIONAL, RATE, false); private static final SwaptionPhysicalFixedIborDefinition SWAPTION_DEFINITION_LONG_REC = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, SWAP_DEFINITION_REC, true); private static final SwaptionPhysicalFixedIbor SWAPTION_LONG_REC = SWAPTION_DEFINITION_LONG_REC.toDerivative(REFERENCE_DATE); // Method - calculator private static final double TOLERANCE_PV = 1.0E-2; private static final double TOLERANCE_PV_DELTA = 1.0E+2; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. private static final SwaptionPhysicalFixedIborBlackMethod METHOD_BLACK = SwaptionPhysicalFixedIborBlackMethod.getInstance(); private static final SwapFixedCouponDiscountingMethod METHOD_SWAP = SwapFixedCouponDiscountingMethod.getInstance(); private static final ParRateDiscountingCalculator PRDC = ParRateDiscountingCalculator.getInstance(); private static final PresentValueDiscountingCalculator PVDC = PresentValueDiscountingCalculator.getInstance(); private static final PresentValueBlackSwaptionCalculator PVBSC = PresentValueBlackSwaptionCalculator.getInstance(); private static final PresentValueCurveSensitivityBlackSwaptionCalculator PVCSBSC = PresentValueCurveSensitivityBlackSwaptionCalculator.getInstance(); private static final PresentValueBlackSensitivityBlackSwaptionCalculator PVBSSBSC = PresentValueBlackSensitivityBlackSwaptionCalculator.getInstance(); private static final double SHIFT = 1.0E-6; private static final ParameterSensitivityParameterCalculator<BlackSwaptionFlatProviderInterface> PS_BS_C = new ParameterSensitivityParameterCalculator<>(PVCSBSC); private static final ParameterSensitivityBlackSwaptionDiscountInterpolatedFDCalculator PS_BS_FDC = new ParameterSensitivityBlackSwaptionDiscountInterpolatedFDCalculator(PVBSC, SHIFT); private static final BlackSwaptionSensitivityNodeCalculator BSSNC = new BlackSwaptionSensitivityNodeCalculator(); @Test public void presentValue() { final MultipleCurrencyAmount pvMethod = METHOD_BLACK.presentValue(SWAPTION_LONG_REC, BLACK_MULTICURVES); final double forward = SWAPTION_LONG_REC.getUnderlyingSwap().accept(PRDC, MULTICURVES); final double pvbp = METHOD_SWAP.presentValueBasisPoint(SWAPTION_LONG_REC.getUnderlyingSwap(), MULTICURVES); final double volatility = BLACK.getVolatility( SWAPTION_LONG_REC.getTimeToExpiry(), SWAPTION_LONG_REC.getMaturityTime()); final BlackPriceFunction blackFunction = new BlackPriceFunction(); final BlackFunctionData dataBlack = new BlackFunctionData(forward, pvbp, volatility); final Function1D<BlackFunctionData, Double> func = blackFunction.getPriceFunction(SWAPTION_LONG_REC); final double pvExpected = func.evaluate(dataBlack); assertEquals( "Swaption Black method: present value", pvExpected, pvMethod.getAmount(EUR), TOLERANCE_PV); } @Test /** Tests the payer/receiver parity for swaptions present value. */ public void presentValuePayerReceiverParity() { final SwapFixedIborDefinition swapDefinitionPay = SwapFixedIborDefinition.from( SETTLE_DATE, SWAP_TENOR, GENERATOR_EUR1YEURIBOR6M, NOTIONAL, RATE, true); final SwaptionPhysicalFixedIborDefinition swaptionDefinitionShortPayer = SwaptionPhysicalFixedIborDefinition.from(EXPIRY_DATE, swapDefinitionPay, false); final SwaptionPhysicalFixedIbor swaptionShortPayer = swaptionDefinitionShortPayer.toDerivative(REFERENCE_DATE); final InstrumentDerivative swapRec = SWAP_DEFINITION_REC.toDerivative(REFERENCE_DATE); final MultipleCurrencyAmount pvLR = METHOD_BLACK.presentValue(SWAPTION_LONG_REC, BLACK_MULTICURVES); final MultipleCurrencyAmount pvSP = METHOD_BLACK.presentValue(swaptionShortPayer, BLACK_MULTICURVES); final MultipleCurrencyAmount pvSwap = swapRec.accept(PVDC, MULTICURVES); assertEquals( "Swaption Black method: present value", pvSwap.getAmount(EUR), pvLR.getAmount(EUR) + pvSP.getAmount(EUR), TOLERANCE_PV); } @Test /** Compare the method figures to the Calculator figures. */ public void presentValueMethodVsCalculator() { final MultipleCurrencyAmount pvMethod = METHOD_BLACK.presentValue(SWAPTION_LONG_REC, BLACK_MULTICURVES); final MultipleCurrencyAmount pvCalculator = SWAPTION_LONG_REC.accept(PVBSC, BLACK_MULTICURVES); assertEquals( "Swaption Black method: present value", pvCalculator.getAmount(EUR), pvMethod.getAmount(EUR), TOLERANCE_PV); } @Test /** Tests the curve sensitivity for the explicit formula. */ public void presentValueCurveSensitivity() { final MultipleCurrencyParameterSensitivity pvpsExact = PS_BS_C.calculateSensitivity( SWAPTION_LONG_REC, BLACK_MULTICURVES, BLACK_MULTICURVES.getMulticurveProvider().getAllNames()); final MultipleCurrencyParameterSensitivity pvpsFD = PS_BS_FDC.calculateSensitivity(SWAPTION_LONG_REC, BLACK_MULTICURVES); AssertSensivityObjects.assertEquals( "Swaption Black method: presentValueCurveSensitivity ", pvpsExact, pvpsFD, TOLERANCE_PV_DELTA); } @Test /** Compare the method figures to the Calculator figures. */ public void presentValueCurveSensitivityMethodVsCalculator() { final MultipleCurrencyMulticurveSensitivity pvcsMethod = METHOD_BLACK.presentValueCurveSensitivity(SWAPTION_LONG_REC, BLACK_MULTICURVES); final MultipleCurrencyMulticurveSensitivity pvcsCalculator = SWAPTION_LONG_REC.accept(PVCSBSC, BLACK_MULTICURVES); AssertSensivityObjects.assertEquals( "Swaption Black method: present value", pvcsMethod, pvcsCalculator, TOLERANCE_PV_DELTA); } @Test /** Tests the Black volatility sensitivity (vega). */ public void presentValueBlackSensitivity() { final double shift = 1.0E-6; final PresentValueBlackSwaptionSensitivity pvbvs = METHOD_BLACK.presentValueBlackSensitivity(SWAPTION_LONG_REC, BLACK_MULTICURVES); final BlackFlatSwaptionParameters BlackP = BlackDataSets.createBlackSwaptionEUR6Shift(shift); final BlackSwaptionFlatProviderDiscount curvesBlackP = new BlackSwaptionFlatProviderDiscount(MULTICURVES, BlackP); final MultipleCurrencyAmount pvP = METHOD_BLACK.presentValue(SWAPTION_LONG_REC, curvesBlackP); final BlackFlatSwaptionParameters BlackM = BlackDataSets.createBlackSwaptionEUR6Shift(-shift); final BlackSwaptionFlatProviderDiscount curvesBlackM = new BlackSwaptionFlatProviderDiscount(MULTICURVES, BlackM); final MultipleCurrencyAmount pvM = METHOD_BLACK.presentValue(SWAPTION_LONG_REC, curvesBlackM); final DoublesPair point = new DoublesPair(SWAPTION_LONG_REC.getTimeToExpiry(), SWAPTION_LONG_REC.getMaturityTime()); assertEquals( "Swaption Black method: present value volatility sensitivity", (pvP.getAmount(EUR) - pvM.getAmount(EUR)) / (2 * shift), pvbvs.getSensitivity().getMap().get(point), TOLERANCE_PV_DELTA); } @Test /** Tests the Black volatility sensitivity (vega). */ public void presentValueBlackSensitivityMethodVsCalculator() { final PresentValueBlackSwaptionSensitivity pvbsMethod = METHOD_BLACK.presentValueBlackSensitivity(SWAPTION_LONG_REC, BLACK_MULTICURVES); final PresentValueBlackSwaptionSensitivity pvbsCalculator = SWAPTION_LONG_REC.accept(PVBSSBSC, BLACK_MULTICURVES); assertEquals("Swaption Black method: present value", pvbsMethod, pvbsCalculator); } @Test /** Tests the Black volatility sensitivity (vega). */ public void presentValueBlackNodeSensitivity() { final double shift = 1.0E-6; final PresentValueBlackSwaptionSensitivity pvbvs = METHOD_BLACK.presentValueBlackSensitivity(SWAPTION_LONG_REC, BLACK_MULTICURVES); final PresentValueBlackSwaptionSensitivity pvbns = BSSNC.calculateNodeSensitivities(pvbvs, BLACK); final double[] x = ((InterpolatedDoublesSurface) BLACK.getVolatilitySurface()).getXDataAsPrimitive(); final double[] y = ((InterpolatedDoublesSurface) BLACK.getVolatilitySurface()).getYDataAsPrimitive(); for (int loopindex = 0; loopindex < x.length; loopindex++) { final BlackFlatSwaptionParameters BlackP = BlackDataSets.createBlackSwaptionEUR6Shift(loopindex, shift); final BlackSwaptionFlatProviderDiscount curvesBlackP = new BlackSwaptionFlatProviderDiscount(MULTICURVES, BlackP); final MultipleCurrencyAmount pvP = METHOD_BLACK.presentValue(SWAPTION_LONG_REC, curvesBlackP); final BlackFlatSwaptionParameters BlackM = BlackDataSets.createBlackSwaptionEUR6Shift(loopindex, -shift); final BlackSwaptionFlatProviderDiscount curvesBlackM = new BlackSwaptionFlatProviderDiscount(MULTICURVES, BlackM); final MultipleCurrencyAmount pvM = METHOD_BLACK.presentValue(SWAPTION_LONG_REC, curvesBlackM); assertEquals( "Swaption Black method: present value volatility sensitivity", (pvP.getAmount(EUR) - pvM.getAmount(EUR)) / (2 * shift), pvbns.getSensitivity().getMap().get(new DoublesPair(x[loopindex], y[loopindex])), TOLERANCE_PV_DELTA); } } }