/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.irfutureoption;
import static com.opengamma.sesame.config.ConfigBuilder.argument;
import static com.opengamma.sesame.config.ConfigBuilder.arguments;
import static com.opengamma.sesame.config.ConfigBuilder.config;
import static com.opengamma.sesame.config.ConfigBuilder.function;
import static com.opengamma.sesame.config.ConfigBuilder.implementations;
import static com.opengamma.util.result.ResultTestUtils.assertSuccess;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.closeTo;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.threeten.bp.Instant;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalTime;
import org.threeten.bp.OffsetTime;
import org.threeten.bp.Period;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.opengamma.analytics.financial.model.volatility.surface.VolatilitySurface;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.GridInterpolator2D;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.interpolation.Interpolator1DFactory;
import com.opengamma.analytics.math.surface.InterpolatedDoublesSurface;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.link.ConfigLink;
import com.opengamma.core.position.Counterparty;
import com.opengamma.core.position.impl.SimpleCounterparty;
import com.opengamma.core.position.impl.SimpleTrade;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.engine.marketdata.spec.LiveMarketDataSpecification;
import com.opengamma.financial.analytics.curve.ConfigDBCurveConstructionConfigurationSource;
import com.opengamma.financial.analytics.curve.CurveConstructionConfigurationSource;
import com.opengamma.financial.analytics.model.fixedincome.BucketedCurveSensitivities;
import com.opengamma.financial.currency.CurrencyMatrix;
import com.opengamma.financial.security.future.InterestRateFutureSecurity;
import com.opengamma.financial.security.option.EuropeanExerciseType;
import com.opengamma.financial.security.option.ExerciseType;
import com.opengamma.financial.security.option.IRFutureOptionSecurity;
import com.opengamma.financial.security.option.OptionType;
import com.opengamma.id.ExternalId;
import com.opengamma.master.security.SecurityDocument;
import com.opengamma.master.security.SecurityMaster;
import com.opengamma.master.security.impl.MasterSecuritySource;
import com.opengamma.service.ServiceContext;
import com.opengamma.service.ThreadLocalServiceContext;
import com.opengamma.service.VersionCorrectionProvider;
import com.opengamma.sesame.CurveDefinitionCurveLabellingFn;
import com.opengamma.sesame.CurveDefinitionFn;
import com.opengamma.sesame.CurveLabellingFn;
import com.opengamma.sesame.CurveNodeConverterFn;
import com.opengamma.sesame.CurveSelector;
import com.opengamma.sesame.CurveSelectorMulticurveBundleFn;
import com.opengamma.sesame.CurveSpecificationFn;
import com.opengamma.sesame.CurveSpecificationMarketDataFn;
import com.opengamma.sesame.DefaultCurveDefinitionFn;
import com.opengamma.sesame.DefaultCurveNodeConverterFn;
import com.opengamma.sesame.DefaultCurveSpecificationFn;
import com.opengamma.sesame.DefaultCurveSpecificationMarketDataFn;
import com.opengamma.sesame.DefaultFXMatrixFn;
import com.opengamma.sesame.DefaultFixingsFn;
import com.opengamma.sesame.DiscountingMulticurveCombinerFn;
import com.opengamma.sesame.Environment;
import com.opengamma.sesame.ExposureFunctionsDiscountingMulticurveCombinerFn;
import com.opengamma.sesame.FXMatrixFn;
import com.opengamma.sesame.FixingsFn;
import com.opengamma.sesame.MarketExposureSelector;
import com.opengamma.sesame.RootFinderConfiguration;
import com.opengamma.sesame.TestMarketDataFactory;
import com.opengamma.sesame.component.RetrievalPeriod;
import com.opengamma.sesame.config.FunctionModelConfig;
import com.opengamma.sesame.engine.CalculationArguments;
import com.opengamma.sesame.engine.ComponentMap;
import com.opengamma.sesame.engine.FixedInstantVersionCorrectionProvider;
import com.opengamma.sesame.engine.FunctionRunner;
import com.opengamma.sesame.graph.FunctionModel;
import com.opengamma.sesame.interestrate.InterestRateMockSources;
import com.opengamma.sesame.marketdata.DefaultHistoricalMarketDataFn;
import com.opengamma.sesame.marketdata.DefaultMarketDataFn;
import com.opengamma.sesame.marketdata.HistoricalMarketDataFn;
import com.opengamma.sesame.marketdata.MarketDataEnvironment;
import com.opengamma.sesame.marketdata.MarketDataEnvironmentBuilder;
import com.opengamma.sesame.marketdata.MarketDataFn;
import com.opengamma.sesame.marketdata.MarketDataSource;
import com.opengamma.sesame.marketdata.RawId;
import com.opengamma.sesame.marketdata.VolatilitySurfaceId;
import com.opengamma.sesame.marketdata.builders.MarketDataBuilder;
import com.opengamma.sesame.marketdata.builders.MarketDataBuilders;
import com.opengamma.sesame.marketdata.builders.MarketDataEnvironmentFactory;
import com.opengamma.sesame.trade.IRFutureOptionTrade;
import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.util.function.Function;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;
import com.opengamma.util.result.Result;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Expiry;
/**
* Tests for interest rate future options analytics functions using the black calculator.
*/
@Test(groups = TestGroup.UNIT)
public class IRFutureOptionFnTest {
private static final ZonedDateTime VALUATION_TIME = DateUtils.getUTCDate(2014, 1, 22);
public static final LocalDate TRADE_DATE = LocalDate.of(2000, 1, 1);
public static final OffsetTime TRADE_TIME = OffsetTime.of(LocalTime.of(0, 0), ZoneOffset.UTC);
private static final LocalDate MARKET_DATA_DATE = LocalDate.of(2014, 2, 18);
private static final double STD_TOLERANCE_PV = 1.0E-3;
private IRFutureOptionFn _blackIRFutureOptionFn;
private IRFutureOptionFn _normalIRFutureOptionFn;
private InterestRateFutureSecurity _irFuture = createIRFuture();
private IRFutureOptionTrade _irFutureOptionTrade = createIRFutureOptionTrade();
private FunctionRunner _functionRunner;
private static final CalculationArguments ARGS =
CalculationArguments.builder()
.valuationTime(VALUATION_TIME)
.marketDataSpecification(LiveMarketDataSpecification.LIVE_SPEC)
.build();
private static final Interpolator1D LINEAR_FLAT =
CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.LINEAR,
Interpolator1DFactory.FLAT_EXTRAPOLATOR,
Interpolator1DFactory.FLAT_EXTRAPOLATOR);
private static final GridInterpolator2D INTERPOLATOR_2D = new GridInterpolator2D(LINEAR_FLAT, LINEAR_FLAT);
private static final InterpolatedDoublesSurface TEST_SURFACE = InterpolatedDoublesSurface.from(
new double[] { .1, .2, .3 },
new double[] { .1, .2, .3 },
new double[] { .1, .2, .3 },
INTERPOLATOR_2D
);
private static MarketDataEnvironment createSuppliedData() {
LocalDateDoubleTimeSeries optionPrice = ImmutableLocalDateDoubleTimeSeries.of(VALUATION_TIME.toLocalDate(), 0.975);
RawId<Double> optionRawId = RawId.of(ExternalSchemes.syntheticSecurityId("Test future option").toBundle());
MarketDataEnvironmentBuilder builder = new MarketDataEnvironmentBuilder();
builder.add(optionRawId, optionPrice);
builder.add(VolatilitySurfaceId.of("TestExchange"), new VolatilitySurface(TEST_SURFACE));
builder.valuationTime(VALUATION_TIME);
return builder.build();
}
@BeforeClass
public void setUpClass() {
ImmutableMap<Class<?>, Object> components = generateComponents();
VersionCorrectionProvider vcProvider = new FixedInstantVersionCorrectionProvider(Instant.now());
ServiceContext serviceContext = ServiceContext.of(components).with(VersionCorrectionProvider.class, vcProvider);
ThreadLocalServiceContext.init(serviceContext);
ComponentMap componentMap = ComponentMap.of(components);
MarketDataSource marketDataSource = InterestRateMockSources.createMarketDataSource(MARKET_DATA_DATE, true);
TestMarketDataFactory marketDataFactory = new TestMarketDataFactory(marketDataSource);
ConfigLink<CurrencyMatrix> currencyMatrixLink = ConfigLink.resolved(componentMap.getComponent(CurrencyMatrix.class));
List<MarketDataBuilder> builders = MarketDataBuilders.standard(componentMap, "dataSource", currencyMatrixLink);
MarketDataEnvironmentFactory environmentFactory = new MarketDataEnvironmentFactory(marketDataFactory, builders);
_functionRunner = new FunctionRunner(environmentFactory);
_normalIRFutureOptionFn = FunctionModel.build(IRFutureOptionFn.class, normalConfig(), componentMap);
_blackIRFutureOptionFn = FunctionModel.build(IRFutureOptionFn.class, blackConfig(), componentMap);
}
private FunctionModelConfig blackConfig() {
FunctionModelConfig config =
config(
arguments(
function(
MarketExposureSelector.class,
argument("exposureFunctions", ConfigLink.resolved(InterestRateMockSources.mockExposureFunctions()))),
function(
RootFinderConfiguration.class,
argument("rootFinderAbsoluteTolerance", 1e-9),
argument("rootFinderRelativeTolerance", 1e-9),
argument("rootFinderMaxIterations", 1000)),
function(
DefaultCurveNodeConverterFn.class,
argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1))))),
implementations(
IRFutureOptionFn.class, DefaultIRFutureOptionFn.class,
IRFutureOptionCalculatorFactory.class, IRFutureOptionBlackCalculatorFactory.class,
CurveSpecificationMarketDataFn.class, DefaultCurveSpecificationMarketDataFn.class,
FXMatrixFn.class, DefaultFXMatrixFn.class,
BlackSTIRFuturesProviderFn.class, TestBlackSTIRFuturesProviderFn.class,
DiscountingMulticurveCombinerFn.class, ExposureFunctionsDiscountingMulticurveCombinerFn.class,
CurveDefinitionFn.class, DefaultCurveDefinitionFn.class,
CurveLabellingFn.class, CurveDefinitionCurveLabellingFn.class,
CurveSpecificationFn.class, DefaultCurveSpecificationFn.class,
CurveConstructionConfigurationSource.class, ConfigDBCurveConstructionConfigurationSource.class,
CurveNodeConverterFn.class, DefaultCurveNodeConverterFn.class,
HistoricalMarketDataFn.class, DefaultHistoricalMarketDataFn.class,
FixingsFn.class, DefaultFixingsFn.class,
MarketDataFn.class, DefaultMarketDataFn.class,
CurveSelector.class, MarketExposureSelector.class,
DiscountingMulticurveCombinerFn.class, CurveSelectorMulticurveBundleFn.class));
return config;
}
private FunctionModelConfig normalConfig() {
FunctionModelConfig config =
config(
arguments(
function(
MarketExposureSelector.class,
argument("exposureFunctions", ConfigLink.resolved(InterestRateMockSources.mockExposureFunctions()))),
function(
RootFinderConfiguration.class,
argument("rootFinderAbsoluteTolerance", 1e-9),
argument("rootFinderRelativeTolerance", 1e-9),
argument("rootFinderMaxIterations", 1000)),
function(
TestIRFutureOptionNormalSurfaceProviderFn.class,
argument("moneynessOnPrice", false)),
function(
DefaultCurveNodeConverterFn.class,
argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1))))),
implementations(
IRFutureOptionFn.class, DefaultIRFutureOptionFn.class,
IRFutureOptionCalculatorFactory.class, IRFutureOptionNormalCalculatorFactory.class,
CurveSpecificationMarketDataFn.class, DefaultCurveSpecificationMarketDataFn.class,
FXMatrixFn.class, DefaultFXMatrixFn.class,
CurveDefinitionFn.class, DefaultCurveDefinitionFn.class,
CurveLabellingFn.class, CurveDefinitionCurveLabellingFn.class,
CurveSpecificationFn.class, DefaultCurveSpecificationFn.class,
CurveConstructionConfigurationSource.class, ConfigDBCurveConstructionConfigurationSource.class,
CurveNodeConverterFn.class, DefaultCurveNodeConverterFn.class,
HistoricalMarketDataFn.class, DefaultHistoricalMarketDataFn.class,
FixingsFn.class, DefaultFixingsFn.class,
MarketDataFn.class, DefaultMarketDataFn.class,
CurveSelector.class, MarketExposureSelector.class,
IRFutureOptionNormalSurfaceProviderFn.class, TestIRFutureOptionNormalSurfaceProviderFn.class,
DiscountingMulticurveCombinerFn.class, CurveSelectorMulticurveBundleFn.class));
return config;
}
private ImmutableMap<Class<?>, Object> generateComponents() {
ImmutableMap.Builder<Class<?>, Object> builder = ImmutableMap.builder();
for (Map.Entry<Class<?>, Object> entry: InterestRateMockSources.generateBaseComponents().entrySet()) {
Class<?> key = entry.getKey();
if (key.equals(SecuritySource.class)) {
appendSecuritySource((SecuritySource) entry.getValue());
}
builder.put(key, entry.getValue());
}
return builder.build();
}
// TODO - this assumes knowledge of the underlying source, should find a better way to do this
private void appendSecuritySource(SecuritySource source) {
SecurityMaster master = ((MasterSecuritySource) source).getMaster();
master.add(new SecurityDocument(_irFuture));
}
private InterestRateFutureSecurity createIRFuture() {
Expiry expiry = new Expiry(ZonedDateTime.of(LocalDate.of(2014, 6, 18), LocalTime.of(0, 0), ZoneOffset.UTC));
String tradingExchange = "";
String settlementExchange = "";
Currency currency = Currency.USD;
double unitAmount = 1000;
ExternalId underlyingId = InterestRateMockSources.getLiborIndexId();
String category = "";
InterestRateFutureSecurity irFuture = new InterestRateFutureSecurity(expiry,
tradingExchange,
settlementExchange,
currency,
unitAmount,
underlyingId,
category);
// Need this for time series lookup
ExternalId irFutureId = ExternalSchemes.syntheticSecurityId("Test future");
irFuture.setExternalIdBundle(irFutureId.toBundle());
return irFuture;
}
private IRFutureOptionTrade createIRFutureOptionTrade() {
String exchange = "TestExchange";
ExerciseType exerciseType = new EuropeanExerciseType();
double pointValue = Double.NaN;
boolean margined = true;
double strike = 0.99;
OptionType optionType = OptionType.PUT;
ExternalId irFutureId = Iterables.getOnlyElement(_irFuture.getExternalIdBundle());
IRFutureOptionSecurity irFutureOption = new IRFutureOptionSecurity(exchange,
_irFuture.getExpiry(),
exerciseType,
irFutureId,
pointValue,
margined,
_irFuture.getCurrency(),
strike,
optionType);
// Need this for time series lookup
irFutureOption.setExternalIdBundle(ExternalSchemes.syntheticSecurityId("Test future option").toBundle());
Counterparty counterparty = new SimpleCounterparty(ExternalId.of(Counterparty.DEFAULT_SCHEME, "COUNTERPARTY"));
BigDecimal tradeQuantity = BigDecimal.valueOf(1);
SimpleTrade trade = new SimpleTrade(irFutureOption, tradeQuantity, counterparty, TRADE_DATE, TRADE_TIME);
trade.setPremium(10.0);
trade.setPremiumCurrency(Currency.USD);
return new IRFutureOptionTrade(trade);
}
@Test
public void testBlackPresentValue() {
Result<MultipleCurrencyAmount> result = _functionRunner.runFunction(
ARGS, createSuppliedData(), new Function<Environment, Result<MultipleCurrencyAmount>>() {
@Override
public Result<MultipleCurrencyAmount> apply(Environment env) {
return _blackIRFutureOptionFn.calculatePV(env, _irFutureOptionTrade);
}
});
assertSuccess(result);
MultipleCurrencyAmount mca = result.getValue();
assertThat(mca.getCurrencyAmount(Currency.USD).getAmount(), is(closeTo(-972.460677, STD_TOLERANCE_PV)));
}
@Test
public void testBlackBucketedZeroDelta() {
Result<BucketedCurveSensitivities> result = _functionRunner.runFunction(
ARGS, createSuppliedData(), new Function<Environment, Result<BucketedCurveSensitivities>>() {
@Override
public Result<BucketedCurveSensitivities> apply(Environment env) {
return _blackIRFutureOptionFn.calculateBucketedZeroIRDelta(env, _irFutureOptionTrade);
}
});
assertSuccess(result);
}
@Test
public void testNormalPresentValue() {
Result<MultipleCurrencyAmount> result = _functionRunner.runFunction(
ARGS, createSuppliedData(), new Function<Environment, Result<MultipleCurrencyAmount>>() {
@Override
public Result<MultipleCurrencyAmount> apply(Environment env) {
return _normalIRFutureOptionFn.calculatePV(env, _irFutureOptionTrade);
}
});
assertSuccess(result);
MultipleCurrencyAmount mca = result.getValue();
assertThat(mca.getCurrencyAmount(Currency.USD).getAmount(), is(closeTo(-902.7156551, STD_TOLERANCE_PV)));
}
@Test
public void testNormalPrice() {
Result<Double> result = _functionRunner.runFunction(
ARGS, createSuppliedData(), new Function<Environment,Result<Double>>() {
@Override
public Result<Double> apply(Environment env) {
return _normalIRFutureOptionFn.calculateModelPrice(env, _irFutureOptionTrade);
}
});
assertSuccess(result);
Double price = result.getValue();
assertThat(price, is(closeTo(0.072284344, STD_TOLERANCE_PV)));
}
@Test
public void testNormalBucketedZeroDelta() {
Result<BucketedCurveSensitivities> result = _functionRunner.runFunction(
ARGS, createSuppliedData(), new Function<Environment, Result<BucketedCurveSensitivities>>() {
@Override
public Result<BucketedCurveSensitivities> apply(Environment env) {
return _normalIRFutureOptionFn.calculateBucketedZeroIRDelta(env, _irFutureOptionTrade);
}
});
assertSuccess(result);
}
}