ForexOptionVanillaVannaVolgaMethod { public MultipleCurrencyAmount presentValue(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves) { Validate.notNull(optionForex, "Forex option"); Validate.notNull(smileMulticurves, "Smile"); Validate.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data"); final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider(); final double dfDomestic = multicurves.getDiscountFactor(optionForex.getCurrency2(), optionForex.getUnderlyingForex().getPaymentTime()); final double dfForeign = multicurves.getDiscountFactor(optionForex.getCurrency1(), optionForex.getUnderlyingForex().getPaymentTime()); final double spot = multicurves.getFxRate(optionForex.getCurrency1(), optionForex.getCurrency2()); final double forward = spot * dfForeign / dfDomestic; final SmileDeltaParameters smileAtTime = smileMulticurves.getSmile(optionForex.getCurrency1(), optionForex.getCurrency2(), optionForex.getTimeToExpiry()); final double[] strikesVV = smileAtTime.getStrike(forward); final double[] volVV = smileAtTime.getVolatility(); final double volATM = volVV[1]; final double[] priceVVATM = new double[3]; final double[] priceVVsmile = new double[3]; final BlackFunctionData dataBlackATM = new BlackFunctionData(forward, dfDomestic, volATM); for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) { final BlackFunctionData dataBlackSmile = new BlackFunctionData(forward, dfDomestic, volVV[loopvv]); final EuropeanVanillaOption optionVV = new EuropeanVanillaOption(strikesVV[loopvv], optionForex.getTimeToExpiry(), true); priceVVATM[loopvv] = BLACK_FUNCTION.getPriceFunction(optionVV).evaluate(dataBlackATM); priceVVsmile[loopvv] = BLACK_FUNCTION.getPriceFunction(optionVV).evaluate(dataBlackSmile); } final double priceFlat = BLACK_FUNCTION.getPriceFunction(optionForex).evaluate(dataBlackATM); final double[] x = vannaVolgaWeights(optionForex, forward, dfDomestic, strikesVV, volVV); double price = priceFlat; for (int loopvv = 0; loopvv < 3; loopvv = loopvv + 2) { price += x[loopvv] * (priceVVsmile[loopvv] - priceVVATM[loopvv]); } price *= Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount()) * (optionForex.isLong() ? 1.0 : -1.0); final CurrencyAmount pvCurrency = CurrencyAmount.of(optionForex.getUnderlyingForex().getCurrency2(), price); return MultipleCurrencyAmount.of(pvCurrency); } private  ForexOptionVanillaVannaVolgaMethod(); static ForexOptionVanillaVannaVolgaMethod getInstance(); MultipleCurrencyAmount presentValue(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves); MultipleCurrencyAmount currencyExposure(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves); PresentValueForexBlackVolatilitySensitivity presentValueBlackVolatilitySensitivity(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves); MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final ForexOptionVanilla optionForex, final BlackForexVannaVolgaProviderInterface smileMulticurves); double[] vannaVolgaWeights(final ForexOptionVanilla optionForex, final double forward, final double dfDomestic, final double[] strikesReference, final double[] volatilitiesReference); double[] vannaVolgaWeights(final ForexOptionVanilla optionForex, final double forward, final double dfDomestic, final double[] strikesReference, final double[] volatilitiesReference,
      final double[] vega);  }
@Test public void presentValueHardCoded() { final int nbStrike = 10; final double strikeMin = 1.00; final double strikeRange = 0.80; final double[] strikes = new double[nbStrike + 1]; final double[] pvExpected = new double[] {3.860405407112769E7, 3.0897699603079587E7, 2.3542824458812844E7, 1.6993448607300103E7, 1.1705393621236656E7, 7865881.826, 5312495.846, 3680367.677, 2607701.430, 1849818.30, 1282881.98}; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime optionExpiry = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(18), BUSINESS_DAY, CALENDAR); final ZonedDateTime optionPay = ScheduleCalculator.getAdjustedDate(optionExpiry, SETTLEMENT_DAYS, CALENDAR); final ForexOptionVanilla[] forexOption = new ForexOptionVanilla[nbStrike + 1]; for (int loopstrike = 0; loopstrike <= nbStrike; loopstrike++) { strikes[loopstrike] = strikeMin + loopstrike * strikeRange / nbStrike; final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, optionPay, notional, strikes[loopstrike]); final ForexOptionVanillaDefinition forexOptionDefinition = new ForexOptionVanillaDefinition(forexUnderlyingDefinition, optionExpiry, isCall, isLong); forexOption[loopstrike] = forexOptionDefinition.toDerivative(REFERENCE_DATE); } final double[] pvVV = new double[nbStrike + 1]; for (int loopstrike = 0; loopstrike <= nbStrike; loopstrike++) { pvVV[loopstrike] = METHOD_VANNA_VOLGA.presentValue(forexOption[loopstrike], VANNAVOLGA_MULTICURVES).getAmount(USD); assertEquals("Forex vanilla option: present value vanna-volga / hard-coded", pvExpected[loopstrike], pvVV[loopstrike], TOLERANCE_PV); } }
@Test public void presentValueAtMarketData() { final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime optionExpiry = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(18), BUSINESS_DAY, CALENDAR); final ZonedDateTime optionPay = ScheduleCalculator.getAdjustedDate(optionExpiry, SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingSpotDefinition = new ForexDefinition(EUR, USD, optionPay, notional, SPOT); final ForexOptionVanillaDefinition forexOptionSpotDefinition = new ForexOptionVanillaDefinition(forexUnderlyingSpotDefinition, optionExpiry, isCall, isLong); final ForexOptionVanilla forexOptionSpot = forexOptionSpotDefinition.toDerivative(REFERENCE_DATE); final double forward = METHOD_BLACK.forwardForexRate(forexOptionSpot, MULTICURVES); final SmileDeltaParameters smileTime = SMILE_TERM.getSmileForTime(forexOptionSpot.getTimeToExpiry()); final double[] strikes = smileTime.getStrike(forward); final int nbStrike = strikes.length; final ForexOptionVanilla[] forexOption = new ForexOptionVanilla[nbStrike]; for (int loopstrike = 0; loopstrike < nbStrike; loopstrike++) { final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, optionPay, notional, strikes[loopstrike]); final ForexOptionVanillaDefinition forexOptionDefinition = new ForexOptionVanillaDefinition(forexUnderlyingDefinition, optionExpiry, isCall, isLong); forexOption[loopstrike] = forexOptionDefinition.toDerivative(REFERENCE_DATE); } final double[] pvVV = new double[nbStrike]; final double[] pvInt = new double[nbStrike]; for (int loopstrike = 0; loopstrike < nbStrike; loopstrike++) { pvVV[loopstrike] = METHOD_VANNA_VOLGA.presentValue(forexOption[loopstrike], VANNAVOLGA_MULTICURVES).getAmount(USD); pvInt[loopstrike] = METHOD_BLACK.presentValue(forexOption[loopstrike], SMILE_MULTICURVES).getAmount(USD); assertEquals("Forex vanilla option: currency exposure put/call parity domestic", pvInt[loopstrike], pvVV[loopstrike], TOLERANCE_PV); } }
@Test(enabled = false) public void analysisSmileCall() { final int nbStrike = 50; final double strikeMin = 1.00; final double strikeRange = 0.80; final double[] strikes = new double[nbStrike + 1]; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime optionExpiry = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(18), BUSINESS_DAY, CALENDAR); final ZonedDateTime optionPay = ScheduleCalculator.getAdjustedDate(optionExpiry, SETTLEMENT_DAYS, CALENDAR); final ForexOptionVanilla[] forexOption = new ForexOptionVanilla[nbStrike + 1]; for (int loopstrike = 0; loopstrike <= nbStrike; loopstrike++) { strikes[loopstrike] = strikeMin + loopstrike * strikeRange / nbStrike; final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, optionPay, notional, strikes[loopstrike]); final ForexOptionVanillaDefinition forexOptionDefinition = new ForexOptionVanillaDefinition(forexUnderlyingDefinition, optionExpiry, isCall, isLong); forexOption[loopstrike] = forexOptionDefinition.toDerivative(REFERENCE_DATE); } final double[] pvVV = new double[nbStrike + 1]; final double[] pvInt = new double[nbStrike + 1]; final double[] volVV = new double[nbStrike + 1]; final double[] volInt = new double[nbStrike + 1]; for (int loopstrike = 0; loopstrike <= nbStrike; loopstrike++) { pvVV[loopstrike] = METHOD_VANNA_VOLGA.presentValue(forexOption[loopstrike], VANNAVOLGA_MULTICURVES).getAmount(USD); pvInt[loopstrike] = METHOD_BLACK.presentValue(forexOption[loopstrike], SMILE_MULTICURVES).getAmount(USD); final double forward = METHOD_BLACK.forwardForexRate(forexOption[loopstrike], MULTICURVES); final double df = MULTICURVES.getDiscountFactor(USD, forexOption[loopstrike].getUnderlyingForex().getPaymentTime()); volVV[loopstrike] = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(forward, df, 0.20), forexOption[loopstrike], pvVV[loopstrike] / notional); volInt[loopstrike] = METHOD_BLACK.impliedVolatility(forexOption[loopstrike], SMILE_MULTICURVES); } }
@Test(enabled = true) public void analysisAtData() { final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime optionExpiry = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(18), BUSINESS_DAY, CALENDAR); final ZonedDateTime optionPay = ScheduleCalculator.getAdjustedDate(optionExpiry, SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingSpotDefinition = new ForexDefinition(EUR, USD, optionPay, notional, SPOT); final ForexOptionVanillaDefinition forexOptionSpotDefinition = new ForexOptionVanillaDefinition(forexUnderlyingSpotDefinition, optionExpiry, isCall, isLong); final ForexOptionVanilla forexOptionSpot = forexOptionSpotDefinition.toDerivative(REFERENCE_DATE); final double forward = METHOD_BLACK.forwardForexRate(forexOptionSpot, MULTICURVES); final SmileDeltaParameters smileTime = SMILE_TERM.getSmileForTime(forexOptionSpot.getTimeToExpiry()); final double[] strikes = smileTime.getStrike(forward); final int nbStrike = strikes.length; final ForexOptionVanilla[] forexOption = new ForexOptionVanilla[nbStrike]; for (int loopstrike = 0; loopstrike < nbStrike; loopstrike++) { final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, optionPay, notional, strikes[loopstrike]); final ForexOptionVanillaDefinition forexOptionDefinition = new ForexOptionVanillaDefinition(forexUnderlyingDefinition, optionExpiry, isCall, isLong); forexOption[loopstrike] = forexOptionDefinition.toDerivative(REFERENCE_DATE); } final double[] pvVV = new double[nbStrike]; final double[] pvInt = new double[nbStrike]; final double[] volVV = new double[nbStrike]; final double[] volInt = new double[nbStrike]; for (int loopstrike = 0; loopstrike < nbStrike; loopstrike++) { pvVV[loopstrike] = METHOD_VANNA_VOLGA.presentValue(forexOption[loopstrike], VANNAVOLGA_MULTICURVES).getAmount(USD); pvInt[loopstrike] = METHOD_BLACK.presentValue(forexOption[loopstrike], SMILE_MULTICURVES).getAmount(USD); final double df = MULTICURVES.getDiscountFactor(USD, forexOption[loopstrike].getUnderlyingForex().getPaymentTime()); volVV[loopstrike] = BLACK_IMPLIED_VOL.getImpliedVolatility(new BlackFunctionData(forward, df, 0.20), forexOption[loopstrike], pvVV[loopstrike] / notional); volInt[loopstrike] = METHOD_BLACK.impliedVolatility(forexOption[loopstrike], SMILE_MULTICURVES); } }