ForexOptionDigitalBlackSmileMethod { public PresentValueForexBlackVolatilitySensitivity presentValueBlackVolatilitySensitivity(final ForexOptionDigital optionForex, final BlackForexSmileProviderInterface smileMulticurves) { ArgumentChecker.notNull(optionForex, "Forex option"); ArgumentChecker.notNull(smileMulticurves, "Smile"); ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data"); final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider(); final double payTime = optionForex.getUnderlyingForex().getPaymentTime(); final double expiry = optionForex.getExpirationTime(); final Currency domesticCcy; final Currency foreignCcy; final double strike; final double amount; final double omega; if (optionForex.payDomestic()) { domesticCcy = optionForex.getUnderlyingForex().getCurrency2(); foreignCcy = optionForex.getUnderlyingForex().getCurrency1(); strike = optionForex.getStrike(); amount = Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency2().getAmount()); omega = optionForex.isCall() ? 1.0 : -1.0; } else { strike = 1.0 / optionForex.getStrike(); domesticCcy = optionForex.getUnderlyingForex().getCurrency1(); foreignCcy = optionForex.getUnderlyingForex().getCurrency2(); amount = Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()); omega = optionForex.isCall() ? -1.0 : 1.0; } final double dfDomestic = multicurves.getDiscountFactor(domesticCcy, payTime); final double dfForeign = multicurves.getDiscountFactor(foreignCcy, payTime); final double spot = multicurves.getFxRate(foreignCcy, domesticCcy); final double forward = spot * dfForeign / dfDomestic; final double volatility = smileMulticurves.getVolatility(foreignCcy, domesticCcy, optionForex.getExpirationTime(), strike, forward); final double sigmaRootT = volatility * Math.sqrt(expiry); final double dM = Math.log(forward / strike) / sigmaRootT - 0.5 * sigmaRootT; final double pvBar = 1.0; final double dMBar = amount * dfDomestic * NORMAL.getPDF(omega * dM) * (optionForex.isLong() ? 1.0 : -1.0) * omega * pvBar; final double sigmaRootTBar = (-Math.log(forward / strike) / (sigmaRootT * sigmaRootT) - 0.5) * dMBar; final double volatilityBar = Math.sqrt(expiry) * sigmaRootTBar; final DoublesPair point = DoublesPair.of(optionForex.getExpirationTime(), (foreignCcy == smileMulticurves.getCurrencyPair().getFirst()) ? strike : 1.0 / strike); final SurfaceValue result = SurfaceValue.from(point, volatilityBar); final PresentValueForexBlackVolatilitySensitivity sensi = new PresentValueForexBlackVolatilitySensitivity(foreignCcy, domesticCcy, result); return sensi; } private  ForexOptionDigitalBlackSmileMethod(); static ForexOptionDigitalBlackSmileMethod getInstance(); MultipleCurrencyAmount presentValue(final ForexOptionDigital optionForex, final BlackForexSmileProviderInterface smileMulticurves); MultipleCurrencyAmount currencyExposure(final ForexOptionDigital optionForex, final BlackForexSmileProviderInterface smileMulticurves); MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final ForexOptionDigital optionForex, final BlackForexSmileProviderInterface smileMulticurves); PresentValueForexBlackVolatilitySensitivity presentValueBlackVolatilitySensitivity(final ForexOptionDigital optionForex, final BlackForexSmileProviderInterface smileMulticurves); PresentValueForexBlackVolatilityNodeSensitivityDataBundle presentValueBlackVolatilityNodeSensitivity(final ForexOptionDigital optionForex,
      final BlackForexSmileProviderInterface smileMulticurves);  }
@Test public void volatilitySensitivity() { final double shift = 1.0E-6; final PresentValueForexBlackVolatilitySensitivity sensi = METHOD_BLACK_DIGITAL.presentValueBlackVolatilitySensitivity(FOREX_DIGITAL_CALL_DOM, SMILE_MULTICURVES); final Pair<Currency, Currency> currencyPair = Pairs.of(EUR, USD); final DoublesPair point = DoublesPair.of(FOREX_DIGITAL_CALL_DOM.getExpirationTime(), STRIKE); assertEquals("Forex Digital option: vega", currencyPair, sensi.getCurrencyPair()); assertEquals("Forex Digital option: vega size", 1, sensi.getVega().getMap().entrySet().size()); assertTrue("Forex Digital option: vega", sensi.getVega().getMap().containsKey(point)); final double strike = FOREX_DIGITAL_CALL_DOM.getStrike(); final int omega = FOREX_DIGITAL_CALL_DOM.isCall() ? 1 : -1; final double dfDomestic = MULTICURVES.getDiscountFactor(USD, FOREX_DIGITAL_CALL_DOM.getUnderlyingForex().getPaymentTime()); final double dfForeign = MULTICURVES.getDiscountFactor(EUR, FOREX_DIGITAL_CALL_DOM.getUnderlyingForex().getPaymentTime()); final double forward = SPOT * dfForeign / dfDomestic; final double volatility = SMILE_TERM.getVolatility(Triple.of(FOREX_DIGITAL_CALL_DOM.getExpirationTime(), strike, forward)); final double sigmaRootTPlus = (volatility + shift) * Math.sqrt(FOREX_DIGITAL_CALL_DOM.getExpirationTime()); final double dMPlus = Math.log(forward / strike) / sigmaRootTPlus - 0.5 * sigmaRootTPlus; final double pvPlus = Math.abs(FOREX_DIGITAL_CALL_DOM.getUnderlyingForex().getPaymentCurrency2().getAmount()) * dfDomestic * NORMAL.getCDF(omega * dMPlus) * (FOREX_DIGITAL_CALL_DOM.isLong() ? 1.0 : -1.0); final double sigmaRootTMinus = (volatility - shift) * Math.sqrt(FOREX_DIGITAL_CALL_DOM.getExpirationTime()); final double dMMinus = Math.log(forward / strike) / sigmaRootTMinus - 0.5 * sigmaRootTMinus; final double pvMinus = Math.abs(FOREX_DIGITAL_CALL_DOM.getUnderlyingForex().getPaymentCurrency2().getAmount()) * dfDomestic * NORMAL.getCDF(omega * dMMinus) * (FOREX_DIGITAL_CALL_DOM.isLong() ? 1.0 : -1.0); assertEquals("Forex Digital option: vega", (pvPlus - pvMinus) / (2 * shift), sensi.getVega().getMap().get(point), TOLERANCE_PV); final ForexOptionDigitalDefinition optionShortDefinition = new ForexOptionDigitalDefinition(FOREX_DEFINITION, OPTION_EXP_DATE, IS_CALL, !IS_LONG); final ForexOptionDigital optionShort = optionShortDefinition.toDerivative(REFERENCE_DATE); final PresentValueForexBlackVolatilitySensitivity sensiShort = METHOD_BLACK_DIGITAL.presentValueBlackVolatilitySensitivity(optionShort, SMILE_MULTICURVES); assertEquals("Forex Digital option: vega short", -sensi.getVega().getMap().get(point), sensiShort.getVega().getMap().get(point)); final ForexOptionDigitalDefinition optionShortPutDefinition = new ForexOptionDigitalDefinition(FOREX_DEFINITION, OPTION_EXP_DATE, !IS_CALL, IS_LONG); final ForexOptionDigital optionShortPut = optionShortPutDefinition.toDerivative(REFERENCE_DATE); final PresentValueForexBlackVolatilitySensitivity sensiShortPut = METHOD_BLACK_DIGITAL.presentValueBlackVolatilitySensitivity(optionShortPut, SMILE_MULTICURVES); assertEquals("Forex Digital option: vega short", sensiShortPut.getVega().getMap().get(point) + sensi.getVega().getMap().get(point), 0.0, TOLERANCE_PV); }
@Test public void volatilitySensitivityMethodVsCalculator() { final PresentValueForexBlackVolatilitySensitivity pvvsMethod = METHOD_BLACK_DIGITAL.presentValueBlackVolatilitySensitivity(FOREX_DIGITAL_CALL_DOM, SMILE_MULTICURVES); final PresentValueForexBlackVolatilitySensitivity pvvsCalculator = FOREX_DIGITAL_CALL_DOM.accept(PVVSFBSC, SMILE_MULTICURVES); assertEquals("Forex present value curve sensitivity: Method vs Calculator", pvvsMethod, pvvsCalculator); }