/**
* 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 static org.testng.AssertJUnit.assertEquals;
import java.util.Collections;
import java.util.Set;
import org.testng.annotations.Test;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.analytics.financial.greeks.Greek;
import com.opengamma.analytics.financial.greeks.GreekResultCollection;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.model.option.definition.EuropeanVanillaOptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.OptionDefinition;
import com.opengamma.analytics.financial.model.option.definition.StandardOptionDataBundle;
import com.opengamma.analytics.financial.model.volatility.surface.VolatilitySurface;
import com.opengamma.analytics.math.curve.ConstantDoublesCurve;
import com.opengamma.analytics.math.surface.ConstantDoublesSurface;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Expiry;
/**
* Test.
*/
@Test(groups = TestGroup.UNIT)
public class BlackScholesMertonModelTest extends AnalyticOptionModelTest {
private static final ZonedDateTime DATE = DateUtils.getUTCDate(2009, 1, 1);
private static final Expiry ONE_YEAR = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 1));
private static final Expiry NINE_MONTHS = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.75));
private static final Expiry SIX_MONTHS = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.5));
private static final Expiry THREE_MONTHS = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.25));
private static final Expiry EIGHT_DAYS = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 8. / 365));
private static final Expiry ONE_MONTH = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 1. / 12));
private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> MODEL = new BlackScholesMertonModel();
private static final double EPS = 1e-4;
@Test
public void testInputs() {
final OptionDefinition definition = new EuropeanVanillaOptionDefinition(1., EIGHT_DAYS, true);
super.assertInputs(MODEL, definition);
}
@Test
public void testModels() {
final Set<Greek> greeks = Collections.singleton(Greek.FAIR_PRICE);
assertPrices(greeks, 65, THREE_MONTHS, true, 0.08, 0.08, 0.3, 60, getCollection(Greek.FAIR_PRICE, 2.1334));
assertPrices(greeks, 95, SIX_MONTHS, false, 0.1, 0.05, 0.2, 100, getCollection(Greek.FAIR_PRICE, 2.4648));
assertPrices(greeks, 19, NINE_MONTHS, true, 0.1, 0, 0.28, 19, getCollection(Greek.FAIR_PRICE, 1.7011));
assertPrices(greeks, 19, NINE_MONTHS, false, 0.1, 0, 0.28, 19, getCollection(Greek.FAIR_PRICE, 1.7011));
assertPrices(greeks, 3800, NINE_MONTHS, false, 0, 0, 0.15, 4200, getCollection(Greek.FAIR_PRICE, 65.6185));
assertPrices(greeks, 1.6, SIX_MONTHS, true, 0.06, -0.02, 0.12, 1.56, getCollection(Greek.FAIR_PRICE, 0.0291));
}
@Test
public void testGreeks() {
assertGreek(Greek.DELTA, 100, SIX_MONTHS, true, 0.1, 0, 0.36, 105, getCollection(Greek.DELTA, 0.5946));
assertGreek(Greek.DELTA, 100, SIX_MONTHS, false, 0.1, 0, 0.36, 105, getCollection(Greek.DELTA, -0.3566));
assertGreek(Greek.VANNA, 80, THREE_MONTHS, false, 0.05, 0.05, 0.2, 90, getCollection(Greek.VANNA, -1.0008));
assertGreek(Greek.DELTA_BLEED, 90, THREE_MONTHS, false, 0.14, 0, 0.24, 105, getCollection(Greek.DELTA_BLEED, 0.37));
assertGreek(Greek.ELASTICITY, 100, SIX_MONTHS, false, 0.1, 0, 0.36, 105, getCollection(Greek.ELASTICITY, -4.8775));
assertGreek(Greek.GAMMA, 60, NINE_MONTHS, false, 0.1, 0.1, 0.3, 55, getCollection(Greek.GAMMA, 0.0278));
assertGreek(Greek.GAMMA, 60, NINE_MONTHS, true, 0.1, 0.1, 0.3, 55, getCollection(Greek.GAMMA, 0.0278));
assertGreek(Greek.GAMMA_P, 50, EIGHT_DAYS, false, 0.12, 0.12, 0.15, 50, getCollection(Greek.GAMMA_P, 0.1781));
assertGreek(Greek.GAMMA_P, 50, EIGHT_DAYS, false, 0.12, 0.12, 0.15, 50, getCollection(Greek.GAMMA_P, 0.1781));
assertGreek(Greek.ZOMMA, 80, THREE_MONTHS, false, 0.05, 0, 0.26, 100, getCollection(Greek.ZOMMA, 0.0463));
assertGreek(Greek.ZOMMA_P, 80, THREE_MONTHS, false, 0.05, 0, 0.26, 100, getCollection(Greek.ZOMMA_P, 0.0463));
assertGreek(Greek.SPEED, 48, ONE_MONTH, false, 0.06, 0.01, 0.2, 50, getCollection(Greek.SPEED, -0.0291));
assertGreek(Greek.SPEED, 48, ONE_MONTH, true, 0.06, 0.01, 0.2, 50, getCollection(Greek.SPEED, -0.0291));
assertGreek(Greek.SPEED_P, 48, ONE_MONTH, false, 0.06, 0.01, 0.2, 50, getCollection(Greek.SPEED_P, -0.0135));
assertGreek(Greek.SPEED_P, 48, ONE_MONTH, true, 0.06, 0.01, 0.2, 50, getCollection(Greek.SPEED_P, -0.0135));
assertGreek(Greek.VEGA, 60, NINE_MONTHS, true, 0.105, 0.0695, 0.3, 55, getCollection(Greek.VEGA, 18.5027));
assertGreek(Greek.VEGA, 60, NINE_MONTHS, false, 0.105, 0.0695, 0.3, 55, getCollection(Greek.VEGA, 18.5027));
assertGreek(Greek.VOMMA, 130, NINE_MONTHS, false, 0.05, 0, 0.28, 90, getCollection(Greek.VOMMA, 92.3444));
assertGreek(Greek.VOMMA_P, 130, NINE_MONTHS, false, 0.05, 0, 0.28, 90, getCollection(Greek.VOMMA_P, 2.5856));
assertGreek(Greek.THETA, 405, ONE_MONTH, false, 0.07, 0.02, 0.2, 430, getCollection(Greek.THETA, -31.1924));
assertGreek(Greek.DRIFTLESS_THETA, 405, ONE_MONTH, false, 0.07, 0.02, 0.2, 430, getCollection(Greek.DRIFTLESS_THETA, -32.6218));
assertGreek(Greek.DRIFTLESS_THETA, 405, ONE_MONTH, true, 0.07, 0.02, 0.2, 430, getCollection(Greek.DRIFTLESS_THETA, -32.6218));
assertGreek(Greek.RHO, 75, ONE_YEAR, true, 0.09, 0.09, 0.19, 72, getCollection(Greek.RHO, 38.7325));
assertGreek(Greek.PHI, 453, SIX_MONTHS, false, 0.1068, 0.03, 0.28, 733, getCollection(Greek.PHI, 1.6180));
assertGreek(Greek.CARRY_RHO, 490, THREE_MONTHS, false, 0.08, 0.03, 0.15, 500, getCollection(Greek.CARRY_RHO, -42.2254));
assertGreek(Greek.ZETA, 95, THREE_MONTHS, false, 0.08, 0, 0.12, 100, getCollection(Greek.ZETA, 0.2047));
assertGreek(Greek.ZETA, 95, THREE_MONTHS, true, 0.08, 0, 0.12, 100, getCollection(Greek.ZETA, 0.7953));
}
private GreekResultCollection getCollection(final Greek greek, final double value) {
final GreekResultCollection collection = new GreekResultCollection();
collection.put(greek, value);
return collection;
}
private void assertPrices(final Set<Greek> greeks, final double strike, final Expiry expiry, final boolean isCall, final double r, final double b, final double sigma, final double spot,
final GreekResultCollection expected) {
final EuropeanVanillaOptionDefinition definition = new EuropeanVanillaOptionDefinition(strike, expiry, isCall);
final StandardOptionDataBundle data = new StandardOptionDataBundle(YieldCurve.from(ConstantDoublesCurve.from(r)), b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), spot, DATE);
final GreekResultCollection result = MODEL.getGreeks(definition, data, greeks);
assertResults(result, expected);
assertPutCallParity(strike, expiry, r, b, sigma, spot);
}
private void assertGreek(final Greek greek, final double strike, final Expiry expiry, final boolean isCall, final double r, final double b, final double sigma, final double spot,
final GreekResultCollection expected) {
final EuropeanVanillaOptionDefinition definition = new EuropeanVanillaOptionDefinition(strike, expiry, isCall);
final StandardOptionDataBundle data = new StandardOptionDataBundle(YieldCurve.from(ConstantDoublesCurve.from(r)), b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), spot, DATE);
final GreekResultCollection result = MODEL.getGreeks(definition, data, Collections.singleton(greek));
assertResults(result, expected);
}
private void assertPutCallParity(final double strike, final Expiry expiry, final double r, final double b, final double sigma, final double spot) {
final Set<Greek> greeks = Collections.singleton(Greek.FAIR_PRICE);
final EuropeanVanillaOptionDefinition call = new EuropeanVanillaOptionDefinition(strike, expiry, true);
final EuropeanVanillaOptionDefinition put = new EuropeanVanillaOptionDefinition(strike, expiry, false);
final StandardOptionDataBundle data = new StandardOptionDataBundle(YieldCurve.from(ConstantDoublesCurve.from(r)), b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), spot, DATE);
final GreekResultCollection callResult = MODEL.getGreeks(call, data, greeks);
final GreekResultCollection putResult = MODEL.getGreeks(put, data, greeks);
final Double c = callResult.values().iterator().next();
final Double p = putResult.values().iterator().next();
final double t = call.getTimeToExpiry(DATE);
assertEquals(c, p + spot * Math.exp(t * (b - r)) - strike * Math.exp(-r * t), EPS);
}
}