EuropeanOptionOnEuropeanVanillaOptionModel 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 t2 = definition.getUnderlyingOption().getTimeToExpiry(date); final double deltaT = t2 - t1; final double sigma = data.getVolatility(t1, k1); final double r = data.getInterestRate(t1); final double b = data.getCostOfCarry(); final double criticalValue = getCriticalValue(new EuropeanVanillaOptionDefinition(k2, new Expiry(DateUtils.getDateOffsetWithYearFraction(date, deltaT)), underlying.isCall()), data, k1); final double d1 = getD1(s, criticalValue, t1, sigma, b); final double d2 = getD2(d1, sigma, t1); final double d3 = getD1(s, k2, t2, sigma, b); final double d4 = getD2(d3, sigma, t2); if (definition.isCall()) { final double rho = Math.sqrt(t1 / t2); if (underlying.isCall()) { return s * Math.exp(t2 * (b - r)) * BIVARIATE.getCDF(new double[] {d3, d1, rho}) - k2 * Math.exp(-r * t2) * BIVARIATE.getCDF(new double[] {d4, d2, rho}) - k1 * Math.exp(-r * t1) * NORMAL.getCDF(d2); } return k2 * Math.exp(-r * t2) * BIVARIATE.getCDF(new double[] {-d4, -d2, rho}) - s * Math.exp(t2 * (b - r)) * BIVARIATE.getCDF(new double[] {-d3, -d1, rho}) - k1 * Math.exp(-r * t1) * NORMAL.getCDF(-d2); } final double rho = -Math.sqrt(t1 / t2); if (underlying.isCall()) { return k2 * Math.exp(-r * t2) * BIVARIATE.getCDF(new double[] {d4, -d2, rho}) - s * Math.exp(t2 * (b - r)) * BIVARIATE.getCDF(new double[] {d3, -d1, rho}) + k1 * Math.exp(-r * t1) * NORMAL.getCDF(-d2); } return s * Math.exp(t2 * (b - r)) * BIVARIATE.getCDF(new double[] {-d3, d1, rho}) - k2 * Math.exp(-r * t2) * BIVARIATE.getCDF(new double[] {-d4, d2, rho}) + k1 * Math.exp(-r * t1) * NORMAL.getCDF(d2); } }; }  @Override Function1D<StandardOptionDataBundle, Double> getPricingFunction(final EuropeanOptionOnEuropeanVanillaOptionDefinition definition);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullDefinition() { MODEL.getPricingFunction(null); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL.getPricingFunction(OPTION).evaluate((StandardOptionDataBundle) null); }
@Test public void test() { assertEquals(MODEL.getPricingFunction(OPTION).evaluate(DATA), 21.196, 1e-3); final EuropeanVanillaOptionDefinition call = new EuropeanVanillaOptionDefinition(UNDERLYING_STRIKE, UNDERLYING_EXPIRY, true); final EuropeanVanillaOptionDefinition put = new EuropeanVanillaOptionDefinition(UNDERLYING_STRIKE, EXPIRY, false); final EuropeanOptionOnEuropeanVanillaOptionDefinition callOnCall = new EuropeanOptionOnEuropeanVanillaOptionDefinition(STRIKE, EXPIRY, true, call); final EuropeanOptionOnEuropeanVanillaOptionDefinition putOnCall = new EuropeanOptionOnEuropeanVanillaOptionDefinition(STRIKE, EXPIRY, false, call); assertEquals(MODEL.getPricingFunction(callOnCall).evaluate(DATA) - MODEL.getPricingFunction(putOnCall).evaluate(DATA), BSM.getPricingFunction(call).evaluate(DATA) - STRIKE * Math.exp(-0.08 * 0.25), 1e-3); final EuropeanOptionOnEuropeanVanillaOptionDefinition callOnPut = new EuropeanOptionOnEuropeanVanillaOptionDefinition(STRIKE, EXPIRY, true, put); final EuropeanOptionOnEuropeanVanillaOptionDefinition putOnPut = new EuropeanOptionOnEuropeanVanillaOptionDefinition(STRIKE, EXPIRY, false, put); assertEquals(MODEL.getPricingFunction(callOnPut).evaluate(DATA) - MODEL.getPricingFunction(putOnPut).evaluate(DATA), BSM.getPricingFunction(put).evaluate(DATA) - STRIKE * Math.exp(-0.08 * 0.25), 1e-3); }