ForexOptionDigitalBlackSmileMethod { public MultipleCurrencyAmount presentValue(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 expiry = optionForex.getExpirationTime(); final Currency domesticCcy; final Currency foreignCcy; final double strike; final double dfDomestic; final double dfForeign; final double amount; final double omega; if (optionForex.payDomestic()) { domesticCcy = optionForex.getUnderlyingForex().getCurrency2(); foreignCcy = optionForex.getUnderlyingForex().getCurrency1(); dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), optionForex.getUnderlyingForex().getPaymentTime()); dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), optionForex.getUnderlyingForex().getPaymentTime()); 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(); dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency1(), optionForex.getUnderlyingForex().getPaymentTime()); dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency2(), optionForex.getUnderlyingForex().getPaymentTime()); amount = Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()); omega = optionForex.isCall() ? -1.0 : 1.0; } 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 pv = amount * dfDomestic * NORMAL.getCDF(omega * dM) * (optionForex.isLong() ? 1.0 : -1.0); final CurrencyAmount priceCurrency = CurrencyAmount.of(domesticCcy, pv); return MultipleCurrencyAmount.of(priceCurrency); } 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 persentValueAtGridPoint() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime expiryDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofYears(1), BUSINESS_DAY, CALENDAR, true); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, ScheduleCalculator.getAdjustedDate(expiryDate, SETTLEMENT_DAYS, CALENDAR), notional, strike); final ForexOptionDigitalDefinition forexOptionDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expiryDate, isCall, isLong); final ForexOptionDigital forexOption = forexOptionDefinition.toDerivative(REFERENCE_DATE); final double dfDomestic = MULTICURVES.getDiscountFactor(USD, forexOption.getUnderlyingForex().getPaymentTime()); final double dfForeign = MULTICURVES.getDiscountFactor(EUR, forexOption.getUnderlyingForex().getPaymentTime()); final double forward = SPOT * dfForeign / dfDomestic; final double volatility = SMILE_TERM.getVolatility(Triple.of(TimeCalculator.getTimeBetween(REFERENCE_DATE, expiryDate), strike, forward)); final double sigmaRootT = volatility * Math.sqrt(forexOption.getExpirationTime()); final double dM = Math.log(forward / strike) / sigmaRootT - 0.5 * sigmaRootT; final int omega = isCall ? 1 : -1; final double pvExpected = Math.abs(forexOption.getUnderlyingForex().getPaymentCurrency2().getAmount()) * dfDomestic * NORMAL.getCDF(omega * dM) * (isLong ? 1.0 : -1.0); final MultipleCurrencyAmount pvComputed = METHOD_BLACK_DIGITAL.presentValue(forexOption, SMILE_MULTICURVES); assertEquals("Forex Digital option: present value", pvExpected, pvComputed.getAmount(USD), TOLERANCE_PV); }
@Test public void presentValueDomestic() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final double timeToExpiry = TimeCalculator.getTimeBetween(REFERENCE_DATE, expDate); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition forexOptionDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigital forexOption = forexOptionDefinition.toDerivative(REFERENCE_DATE); final double dfDomestic = MULTICURVES.getDiscountFactor(USD, forexOption.getUnderlyingForex().getPaymentTime()); final double dfForeign = MULTICURVES.getDiscountFactor(EUR, forexOption.getUnderlyingForex().getPaymentTime()); final double forward = SPOT * dfForeign / dfDomestic; final double volatility = SMILE_TERM.getVolatility(Triple.of(timeToExpiry, strike, forward)); final double sigmaRootT = volatility * Math.sqrt(forexOption.getExpirationTime()); final double dM = Math.log(forward / strike) / sigmaRootT - 0.5 * sigmaRootT; final int omega = isCall ? 1 : -1; final double pvExpected = Math.abs(forexOption.getUnderlyingForex().getPaymentCurrency2().getAmount()) * dfDomestic * NORMAL.getCDF(omega * dM) * (isLong ? 1.0 : -1.0); final MultipleCurrencyAmount pvComputed = METHOD_BLACK_DIGITAL.presentValue(forexOption, SMILE_MULTICURVES); assertEquals("Forex Digital option: present value", pvExpected, pvComputed.getAmount(USD), TOLERANCE_PV); }
@Test(enabled = false) public void presentValueDomesticProfile() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, 1.0 / strike, strike); final ForexOptionDigitalDefinition forexOptionDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigital forexOption = forexOptionDefinition.toDerivative(REFERENCE_DATE); final int nbSpot = 50; final double range = 0.75; final double[] spot = new double[nbSpot + 1]; final double[] pv = new double[nbSpot + 1]; final MulticurveProviderDiscount multicurveForex = MULTICURVES.copy(); for (int loopspot = 0; loopspot <= nbSpot; loopspot++) { spot[loopspot] = strike - range + 2.0d * range * loopspot / nbSpot; final FXMatrix fxMatrix = new FXMatrix(EUR, USD, spot[loopspot]); multicurveForex.setForexMatrix(fxMatrix); final BlackForexSmileProviderDiscount smile = new BlackForexSmileProviderDiscount(multicurveForex, SMILE_TERM, Pairs.of(EUR, USD)); pv[loopspot] = METHOD_BLACK_DIGITAL.presentValue(forexOption, smile).getAmount(USD); } }
@Test public void presentValueForeign() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final boolean payDomestic = false; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final double timeToExpiry = TimeCalculator.getTimeBetween(REFERENCE_DATE, expDate); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition forexOptionDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong, payDomestic); final ForexOptionDigital forexOption = forexOptionDefinition.toDerivative(REFERENCE_DATE); final double dfDomestic = MULTICURVES.getDiscountFactor(EUR, forexOption.getUnderlyingForex().getPaymentTime()); final double dfForeign = MULTICURVES.getDiscountFactor(USD, forexOption.getUnderlyingForex().getPaymentTime()); final double forward = 1 / SPOT * dfForeign / dfDomestic; final double volatility = SMILE_TERM.getVolatility(Triple.of(timeToExpiry, strike, 1 / forward)); final double sigmaRootT = volatility * Math.sqrt(forexOption.getExpirationTime()); final double dM = Math.log(forward * strike) / sigmaRootT - 0.5 * sigmaRootT; final double omega = isCall ? -1.0 : 1.0; final double pvExpected = Math.abs(forexOption.getUnderlyingForex().getPaymentCurrency1().getAmount()) * dfDomestic * NORMAL.getCDF(omega * dM) * (isLong ? 1.0 : -1.0); final MultipleCurrencyAmount pvComputed = METHOD_BLACK_DIGITAL.presentValue(forexOption, SMILE_MULTICURVES); assertEquals("Forex Digital option: present value", pvExpected, pvComputed.getAmount(EUR), TOLERANCE_PV); }
@Test(enabled = false) public void presentValueDomesticForeign() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, 1.0, strike); final ForexOptionDigitalDefinition forexOptionDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong, false); final ForexOptionDigital forexOption = forexOptionDefinition.toDerivative(REFERENCE_DATE); final int nbSpot = 50; final double range = 0.75; final double[] spot = new double[nbSpot + 1]; final double[] pv = new double[nbSpot + 1]; final MulticurveProviderDiscount multicurveForex = MULTICURVES.copy(); for (int loopspot = 0; loopspot <= nbSpot; loopspot++) { spot[loopspot] = strike - range + 2.0d * range * loopspot / nbSpot; final FXMatrix fxMatrix = new FXMatrix(EUR, USD, spot[loopspot]); multicurveForex.setForexMatrix(fxMatrix); final BlackForexSmileProviderDiscount smile = new BlackForexSmileProviderDiscount(multicurveForex, SMILE_TERM, Pairs.of(EUR, USD)); pv[loopspot] = METHOD_BLACK_DIGITAL.presentValue(forexOption, smile).getAmount(EUR); } }
@Test public void presentValuePutCallParity() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition callDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigitalDefinition putDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong); final ForexOptionDigital call = callDefinition.toDerivative(REFERENCE_DATE); final ForexOptionDigital put = putDefinition.toDerivative(REFERENCE_DATE); final MultipleCurrencyAmount pvCall = METHOD_BLACK_DIGITAL.presentValue(call, SMILE_MULTICURVES); final MultipleCurrencyAmount pvPut = METHOD_BLACK_DIGITAL.presentValue(put, SMILE_MULTICURVES); final MultipleCurrencyAmount pvCash = put.getUnderlyingForex().getPaymentCurrency2().accept(PVDC, MULTICURVES); assertEquals("Forex Digital option: present value", pvCall.getAmount(USD) + pvPut.getAmount(USD), Math.abs(pvCash.getAmount(USD)), TOLERANCE_PV); }
@Test public void presentValueMethodVsCalculator() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition forexOptionDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigital forexOption = forexOptionDefinition.toDerivative(REFERENCE_DATE); final MultipleCurrencyAmount pvMethod = METHOD_BLACK_DIGITAL.presentValue(forexOption, SMILE_MULTICURVES); final MultipleCurrencyAmount pvCalculator = forexOption.accept(PVFBC, SMILE_MULTICURVES); assertEquals("Forex Digital option: present value Method vs Calculator", pvMethod.getAmount(USD), pvCalculator.getAmount(USD), TOLERANCE_PV); }