FXVannaVolgaVolatilityCurveModel implements VolatilityCurveModel<FXVannaVolgaVolatilityCurveDataBundle, FXOptionDataBundle> { @Override public VolatilityCurve getCurve(final FXVannaVolgaVolatilityCurveDataBundle marketQuotes, final FXOptionDataBundle data) { Validate.notNull(marketQuotes); Validate.notNull(data); final double sigmaRR = marketQuotes.getRiskReversal(); final double sigmaATM = marketQuotes.getAtTheMoney(); final double sigmaVWB = marketQuotes.getVegaWeightedButterfly(); final double sigmaDeltaCall = sigmaVWB + sigmaATM + 0.5 * sigmaRR; final double sigmaDeltaPut = sigmaDeltaCall - sigmaRR; final double t = DateUtils.getDifferenceInYears(data.getDate(), marketQuotes.getMaturity()); if (t < 0) { throw new IllegalArgumentException("Cannot have date after time to maturity"); } final double sqrtT = Math.sqrt(t); final double s = data.getSpot(); final double rd = data.getInterestRate(t); final double rf = data.getForeignInterestRate(t); final double alpha = -NORMAL.getInverseCDF(Math.exp(rf * t) * marketQuotes.getDelta()); final double k1 = s * Math.exp(-alpha * sigmaDeltaPut * sqrtT + t * (rd - rf + 0.5 * sigmaDeltaPut * sigmaDeltaPut)); final double k2 = s * Math.exp(t * (rd - rf + 0.5 * sigmaATM * sigmaATM)); final double k3 = s * Math.exp(alpha * sigmaDeltaCall * sqrtT + t * (rd - rf + 0.5 * sigmaDeltaCall * sigmaDeltaCall)); final double lnk21 = Math.log(k2 / k1); final double lnk31 = Math.log(k3 / k1); final double lnk32 = Math.log(k3 / k2); final double sigma = sigmaATM; return new VolatilityCurve(FunctionalDoublesCurve.from(new Function1D<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final Double x) { Validate.notNull(x); final double k = x; final double a1 = Math.log(k2 / k) * Math.log(k3 / k) / lnk21 / lnk31; final double a2 = Math.log(k / k1) * Math.log(k3 / k) / lnk21 / lnk32; final double a3 = Math.log(k / k1) * Math.log(k / k2) / lnk31 / lnk32; final double x1 = a1 * sigmaDeltaPut; final double x2 = a2 * sigmaATM; final double x3 = a3 * sigmaDeltaCall; final double e1 = x1 + x2 + x3 - sigma; final double d1k1 = getD1(s, k1, t, rd, rf, sigma, sqrtT); final double d1k2 = getD1(s, k2, t, rd, rf, sigma, sqrtT); final double d1k3 = getD1(s, k3, t, rd, rf, sigma, sqrtT); final double x4 = a1 * d1k1 * getD2(d1k1, sigma, sqrtT) * (sigmaDeltaPut - sigma) * (sigmaDeltaPut - sigma); final double x5 = a2 * d1k2 * getD2(d1k2, sigma, sqrtT) * (sigmaATM - sigma) * (sigmaATM - sigma); final double x6 = a3 * d1k3 * getD2(d1k3, sigma, sqrtT) * (sigmaDeltaCall - sigma) * (sigmaDeltaCall - sigma); final double e2 = x4 + x5 + x6; final double d1k = getD1(s, k, t, rd, rf, sigma, sqrtT); final double d2k = getD2(d1k, sigma, sqrtT); return sigma + (-sigma + Math.sqrt(sigma * sigma + d1k * d2k * (2 * sigma * e1 + e2))) / d1k / d2k; } })); }  @Override VolatilityCurve getCurve(final FXVannaVolgaVolatilityCurveDataBundle marketQuotes, final FXOptionDataBundle data);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullMarketQuotes() { MODEL.getCurve(null, DATA); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL.getCurve(MARKET_DATA, null); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullPair() { MODEL.getCurve(MARKET_DATA, DATA).getVolatility(null); }
@Test public void test() { final VolatilityCurve curve = MODEL.getCurve(MARKET_DATA, DATA); assertEquals(curve.getVolatility(1.1733), 0.0943, 1e-4); assertEquals(curve.getVolatility(1.2114), 0.0905, 1e-4); assertEquals(curve.getVolatility(1.2487), 0.0893, 1e-4); assertEquals(curve.getVolatility(1e-7), curve.getVolatility(1e-4), 1e-4); assertEquals(curve.getVolatility(5.), curve.getVolatility(6.), 1e-4); }