BlackScholesMertonImpliedVolatilitySurfaceModel implements VolatilitySurfaceModel<Map<OptionDefinition, Double>, StandardOptionDataBundle> { @Override public VolatilitySurface getSurface(final Map<OptionDefinition, Double> optionPrices, final StandardOptionDataBundle optionDataBundle) { Validate.notNull(optionPrices); ArgumentChecker.notEmpty(optionPrices, "option prices"); Validate.notNull(optionDataBundle); if (optionPrices.size() > 1) { s_logger.info("Option price map had more than one entry: using the first pair to imply volatility"); } final Map.Entry<OptionDefinition, Double> entry = optionPrices.entrySet().iterator().next(); final Double price = entry.getValue(); final Function1D<StandardOptionDataBundle, Double> pricingFunction = _bsm.getPricingFunction(entry.getKey()); _rootFinder = new MyBisectionSingleRootFinder(optionDataBundle, price); return _rootFinder.getRoot(pricingFunction, optionDataBundle.withVolatilitySurface(new VolatilitySurface(ConstantDoublesSurface.from(0))), optionDataBundle.withVolatilitySurface(new VolatilitySurface(ConstantDoublesSurface.from(10)))).getVolatilitySurface(); }  @Override VolatilitySurface getSurface(final Map<OptionDefinition, Double> optionPrices, final StandardOptionDataBundle optionDataBundle);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullPrices() { MODEL.getSurface(null, DATA); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testEmptyPrices() { MODEL.getSurface(Collections.<OptionDefinition, Double> emptyMap(), DATA); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL.getSurface(Collections.<OptionDefinition, Double> singletonMap(new EuropeanVanillaOptionDefinition(RANDOM.nextDouble(), new Expiry(DATE), true), 2.3), null); }
@Test public void test() { boolean isCall; double spot, strike, b, price; Expiry expiry; YieldAndDiscountCurve curve; EuropeanVanillaOptionDefinition definition; StandardOptionDataBundle initialData, data; double sigma = 0.01; for (int i = 0; i < 100; i++) { expiry = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, RANDOM.nextDouble() * 2)); sigma += 0.03; spot = 2 * RANDOM.nextDouble() + 10; strike = 2 * RANDOM.nextDouble() + 10; curve = YieldCurve.from(ConstantDoublesCurve.from(RANDOM.nextDouble() / 10)); b = 0; isCall = RANDOM.nextDouble() < 0.5 ? true : false; definition = new EuropeanVanillaOptionDefinition(strike, expiry, isCall); initialData = new StandardOptionDataBundle(curve, b, null, spot, DATE); data = new StandardOptionDataBundle(curve, b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), spot, DATE); price = BSM.getPricingFunction(definition).evaluate(data); assertEquals(sigma, MODEL.getSurface(Collections.<OptionDefinition, Double> singletonMap(definition, price), initialData).getVolatility(DoublesPair.of(0., 0.)), EPS); } }