/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.volatility.surface;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.analytics.financial.model.option.definition.OptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.StandardOptionDataBundle;
import com.opengamma.analytics.math.function.Function;
import com.opengamma.analytics.math.regression.LeastSquaresRegression;
import com.opengamma.analytics.math.regression.LeastSquaresRegressionResult;
import com.opengamma.analytics.math.regression.OrdinaryLeastSquaresRegression;
import com.opengamma.analytics.math.surface.FunctionalDoublesSurface;
import com.opengamma.util.tuple.DoublesPair;
/**
*
*/
public class PractitionerBlackScholesVolatilitySurfaceModel implements VolatilitySurfaceModel<Map<OptionDefinition, Double>, StandardOptionDataBundle> {
private static final Logger s_logger = LoggerFactory.getLogger(PractitionerBlackScholesVolatilitySurfaceModel.class);
private final VolatilitySurfaceModel<Map<OptionDefinition, Double>, StandardOptionDataBundle> _bsmVolatilityModel = new BlackScholesMertonImpliedVolatilitySurfaceModel();
private static final int DEGREE = 5;
private final LeastSquaresRegression _regression;
private static final Double[] EMPTY_ARRAY = new Double[0];
private final Function<Double, double[]> _independentVariableFunction = new Function<Double, double[]>() {
@Override
public double[] evaluate(final Double... x) {
final double t = x[0];
final double k = x[1];
final double[] result = new double[DEGREE];
result[0] = k;
result[1] = k * k;
result[2] = t;
result[3] = t * t;
result[4] = k * t;
return result;
}
};
public PractitionerBlackScholesVolatilitySurfaceModel() {
_regression = new OrdinaryLeastSquaresRegression();
}
@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)))));
}
private LeastSquaresRegressionResult getRegressionResult(final Double[] kArray, final Double[] tArray, final Double[] sigmaArray) {
final int length = kArray.length;
final double[][] x = new double[length][DEGREE];
final double[] y = new double[length];
Double k;
Double t;
Double sigma;
for (int i = 0; i < kArray.length; i++) {
k = kArray[i];
t = tArray[i];
sigma = sigmaArray[i];
x[i] = _independentVariableFunction.evaluate(t, k);
y[i] = sigma;
}
return _regression.regress(x, null, y, true);
}
private class MySurfaceFunction implements Function<Double, Double> {
private final LeastSquaresRegressionResult _result;
public MySurfaceFunction(final LeastSquaresRegressionResult result) {
_result = result;
}
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final Double... tk) {
Validate.notNull(tk, "tk pair");
final double t = tk[0];
final double k = tk[1];
return _result.getPredictedValue(_independentVariableFunction.evaluate(t, k));
}
}
}