PractitionerBlackScholesVolatilitySurfaceModel implements VolatilitySurfaceModel<Map<OptionDefinition, Double>, StandardOptionDataBundle> { @Override public VolatilitySurface getSurface(final Map<OptionDefinition, Double> prices, final StandardOptionDataBundle data) { Validate.notNull(prices, "prices"); Validate.notNull(data, "data"); if (prices.size() < DEGREE) { throw new IllegalArgumentException("Price map contained " + prices.size() + " data point(s); need at least " + DEGREE); } final List<Double> kList = new ArrayList<>(); final List<Double> tList = new ArrayList<>(); final List<Double> sigmaList = new ArrayList<>(); double k, t, sigma; for (final Map.Entry<OptionDefinition, Double> entry : prices.entrySet()) { k = entry.getKey().getStrike(); t = entry.getKey().getTimeToExpiry(data.getDate()); try { sigma = _bsmVolatilityModel.getSurface(Collections.<OptionDefinition, Double>singletonMap(entry.getKey(), entry.getValue()), data).getVolatility(DoublesPair.of(t, k)); kList.add(k); tList.add(t); sigmaList.add(sigma); } catch (final Exception e) { s_logger.info("Problem getting BSM volatility for " + entry.getKey() + ", not using this option in regression. Error was: ", e); } } return new VolatilitySurface(FunctionalDoublesSurface.from(new MySurfaceFunction(getRegressionResult(kList.toArray(EMPTY_ARRAY), tList.toArray(EMPTY_ARRAY), sigmaList.toArray(EMPTY_ARRAY))))); } PractitionerBlackScholesVolatilitySurfaceModel(); @Override VolatilitySurface getSurface(final Map<OptionDefinition, Double> prices, final StandardOptionDataBundle data);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testPriceInput() { MODEL.getSurface(null, new StandardOptionDataBundle(null, 0, null, 0, null)); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testDataInput() { MODEL.getSurface(Collections.<OptionDefinition, Double> emptyMap(), null); }
@Test public void testFlatSurface() { final Map<OptionDefinition, Double> prices = new HashMap<>(); final double sigma = 0.3; OptionDefinition definition; final StandardOptionDataBundle data = new StandardOptionDataBundle(CURVE, B, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), SPOT, DATE); try { MODEL.getSurface(prices, data); Assert.fail(); } catch (final IllegalArgumentException e) { } for (final Expiry expiry : EXPIRY) { for (final double strike : STRIKE) { definition = new EuropeanVanillaOptionDefinition(strike, expiry, IS_CALL); prices.put(definition, BSM.getPricingFunction(definition).evaluate(data)); } } final VolatilitySurface surface = MODEL.getSurface(prices, data); for (final Expiry expiry : TEST_EXPIRY) { for (final double strike : TEST_STRIKE) { assertEquals(surface.getVolatility(DoublesPair.of(DateUtils.getDifferenceInYears(DATE, expiry.getExpiry()), strike)), sigma, EPS); } } }
@Test public void testUnifomlyVaryingSurface() { final Map<OptionDefinition, Double> prices = new HashMap<>(); OptionDefinition definition; StandardOptionDataBundle data = new StandardOptionDataBundle(CURVE, B, null, SPOT, DATE); final double diff = 0.09; final double startSigma = 0.18; final double[] sigma = new double[] {startSigma, startSigma + diff, startSigma + 2 * diff, startSigma + 3 * diff}; for (int i = 0; i < sigma.length; i++) { for (final double strike : STRIKE) { definition = new EuropeanVanillaOptionDefinition(strike, EXPIRY[i], IS_CALL); data = data.withVolatilitySurface(new VolatilitySurface(ConstantDoublesSurface.from(sigma[i]))); prices.put(definition, BSM.getPricingFunction(definition).evaluate(data)); } } final VolatilitySurface surface = MODEL.getSurface(prices, data); double result; Expiry expiry; for (int i = 0; i < TEST_EXPIRY.length; i++) { expiry = TEST_EXPIRY[i]; result = sigma[i] + 4 * diff * DateUtils.getDifferenceInYears(EXPIRY[i].getExpiry(), expiry.getExpiry()); for (final double strike : TEST_STRIKE) { assertEquals(surface.getVolatility(DoublesPair.of(DateUtils.getDifferenceInYears(DATE, expiry.getExpiry()), strike)), result, EPS); } } }