BensoussanCrouhyGalaiOptionOnOptionModel extends AnalyticOptionModel<EuropeanOptionOnEuropeanVanillaOptionDefinition, StandardOptionDataBundle> { @Override public Function1D<StandardOptionDataBundle, Double> getPricingFunction(final EuropeanOptionOnEuropeanVanillaOptionDefinition definition) { Validate.notNull(definition, "definition"); return new Function1D<StandardOptionDataBundle, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final StandardOptionDataBundle data) { Validate.notNull(data, "data"); final double s = data.getSpot(); final OptionDefinition underlying = definition.getUnderlyingOption(); final double k1 = definition.getStrike(); final double k2 = underlying.getStrike(); final ZonedDateTime date = data.getDate(); final double t1 = definition.getTimeToExpiry(date); final double sigma = data.getVolatility(t1, k1); final double r = data.getInterestRate(t1); final double b = data.getCostOfCarry(); final OptionDefinition callDefinition = underlying.isCall() ? underlying : new EuropeanVanillaOptionDefinition(k2, underlying.getExpiry(), true); final GreekResultCollection result = BSM.getGreeks(callDefinition, data, REQUIRED_GREEKS); final double callBSM = result.get(Greek.FAIR_PRICE); final double callDelta = result.get(Greek.DELTA); final double underlyingSigma = sigma * Math.abs(callDelta) * s / callBSM; final double d1 = getD1(callBSM, k1, t1, underlyingSigma, b); final double d2 = getD2(d1, underlyingSigma, t1); final int sign = definition.isCall() ? 1 : -1; if (underlying.isCall()) { return sign * (callBSM * NORMAL.getCDF(sign * d1) - k1 * Math.exp(-r * t1) * NORMAL.getCDF(sign * d2)); } throw new NotImplementedException("This model can only price call-on-call or put-on-call options"); } }; }  @Override Function1D<StandardOptionDataBundle, Double> getPricingFunction(final EuropeanOptionOnEuropeanVanillaOptionDefinition definition);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullDefinition() { BCG.getPricingFunction(null); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { BCG.getPricingFunction(OPTION).evaluate((StandardOptionDataBundle) null); }
@Test public void test() { assertEquals(BCG.getPricingFunction(OPTION).evaluate(DATA), 19.9147, 1e-4); final EuropeanVanillaOptionDefinition underlying = new EuropeanVanillaOptionDefinition(SPOT - 100, UNDERLYING_EXPIRY, true); final EuropeanOptionOnEuropeanVanillaOptionDefinition option = new EuropeanOptionOnEuropeanVanillaOptionDefinition(20, EXPIRY, true, underlying); assertEquals(BCG.getPricingFunction(option).evaluate(DATA) / MODEL.getPricingFunction(option).evaluate(DATA), 1, 1e-2); }