/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.option.pricing.analytic;
import org.apache.commons.lang.Validate;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.analytics.financial.model.option.definition.ExtremeSpreadOptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.StandardOptionWithSpotTimeSeriesDataBundle;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;
import com.opengamma.timeseries.DoubleTimeSeries;
/**
*
*/
public class ExtremeSpreadOptionModel extends AnalyticOptionModel<ExtremeSpreadOptionDefinition, StandardOptionWithSpotTimeSeriesDataBundle> {
private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1);
@Override
public Function1D<StandardOptionWithSpotTimeSeriesDataBundle, Double> getPricingFunction(final ExtremeSpreadOptionDefinition definition) {
Validate.notNull(definition, "definition");
return new Function1D<StandardOptionWithSpotTimeSeriesDataBundle, Double>() {
@SuppressWarnings("synthetic-access")
@Override
public Double evaluate(final StandardOptionWithSpotTimeSeriesDataBundle data) {
Validate.notNull(data, "data");
final double s = data.getSpot();
final double b = data.getCostOfCarry();
final ZonedDateTime date = data.getDate();
final double t1 = -definition.getTimeFromPeriodEnd(date);
final double t2 = definition.getTimeToExpiry(date);
final double r = data.getInterestRate(t2);
final DoubleTimeSeries<?> ts = data.getSpotTimeSeries();
final int eta = definition.isCall() ? 1 : -1;
final int phi = definition.isReverse() ? -1 : 1;
final double x = eta * phi == 1 ? ts.maxValue() : ts.minValue();
final double sigma = data.getVolatility(t2, x); //REVIEW emcleod 21-7-10 works for flat vol surfaces
final double m = Math.log(x / s);
final double mu1 = b - sigma * sigma / 2;
final double mu2 = b + sigma * sigma / 2;
final double df1 = Math.exp(t2 * (b - r));
final double df2 = Math.exp(b * (t1 - t2));
final double df3 = Math.exp(-r * t2);
final double sigmaT2 = sigma * Math.sqrt(t2);
final double sigmaT1 = sigma * Math.sqrt(t1);
final double sigmaSq = sigma * sigma;
final double y = sigmaSq / 2 / b;
if (definition.isReverse()) {
final double dt = t2 - t1;
final double sigmaDT = sigma * Math.sqrt(dt);
final double z1 = s * df1 * (1 + y) * NORMAL.getCDF(eta * (m - mu2 * t2) / sigmaT2);
final double z2 = df3 * x * NORMAL.getCDF(eta * (mu1 * t2 - m) / sigmaT2);
final double z3 = -df3 * x * y * Math.exp(2 * mu1 * m / sigmaSq) * NORMAL.getCDF(eta * (m + mu1 * t2) / sigmaT2);
final double z4 = -s * df1 * (1 + y) * NORMAL.getCDF(-eta * mu2 * dt / sigmaDT);
final double z5 = -df2 * df1 * s * (1 - y) * NORMAL.getCDF(eta * mu1 * dt / sigmaDT);
return -eta * (z1 + z2 + z3 + z4 + z5);
}
final double z1 = s * df1 * (1 + y) * NORMAL.getCDF(eta * (mu2 * t2 - m) / sigmaT2);
final double z2 = -df2 * df1 * s * (1 + y) * NORMAL.getCDF(eta * (mu2 * t1 - m) / sigmaT1);
final double z3 = df3 * x * NORMAL.getCDF(eta * (m - mu1 * t2) / sigmaT2);
final double z4 = -df3 * x * y * Math.exp(2 * mu1 * m / sigmaSq) * NORMAL.getCDF(-eta * (m + mu1 * t2) / sigmaT2);
final double z5 = -df3 * x * NORMAL.getCDF(eta * (m - mu1 * t1) / sigmaT1);
final double z6 = df3 * x * y * Math.exp(2 * mu1 * m / sigmaSq) * NORMAL.getCDF(-eta * (m + mu1 * t1) / sigmaT1);
return eta * (z1 + z2 + z3 + z4 + z5 + z6);
}
};
}
}