/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame;
import static com.opengamma.id.VersionCorrection.LATEST;
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 org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.wire.FudgeMsgReader;
import org.fudgemsg.wire.xml.FudgeXMLStreamReader;
import org.springframework.core.io.ClassPathResource;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZonedDateTime;
import com.google.common.base.Throwables;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.core.config.ConfigSource;
import com.opengamma.core.config.impl.ConfigItem;
import com.opengamma.core.convention.ConventionSource;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.holiday.impl.WeekendHolidaySource;
import com.opengamma.core.security.Security;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.financial.analytics.curve.AbstractCurveDefinition;
import com.opengamma.financial.analytics.curve.CurveConstructionConfiguration;
import com.opengamma.financial.analytics.curve.CurveGroupConfiguration;
import com.opengamma.financial.analytics.curve.CurveNodeIdMapper;
import com.opengamma.financial.analytics.curve.CurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.DiscountingCurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.InterpolatedCurveDefinition;
import com.opengamma.financial.analytics.curve.OvernightCurveTypeConfiguration;
import com.opengamma.financial.convention.FinancialConvention;
import com.opengamma.financial.convention.OISLegConvention;
import com.opengamma.financial.convention.OvernightIndexConvention;
import com.opengamma.financial.convention.SwapFixedLegConvention;
import com.opengamma.financial.currency.CurrencyMatrix;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.VersionCorrection;
import com.opengamma.service.ServiceContext;
import com.opengamma.service.ThreadLocalServiceContext;
import com.opengamma.service.VersionCorrectionProvider;
import com.opengamma.sesame.component.StringSet;
import com.opengamma.sesame.config.FunctionModelConfig;
import com.opengamma.sesame.engine.ComponentMap;
import com.opengamma.sesame.graph.FunctionModel;
import com.opengamma.sesame.marketdata.DefaultMarketDataFn;
import com.opengamma.sesame.marketdata.MarketDataBundle;
import com.opengamma.sesame.marketdata.MarketDataFn;
import com.opengamma.sesame.marketdata.RawId;
import com.opengamma.util.JodaBeanSerialization;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;
import com.opengamma.util.money.Currency;
import com.opengamma.util.result.Result;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.time.Tenor;
/**
* Test for {@code InterpolatedMulticurveBundleFn}.
*/
@Test(groups = TestGroup.UNIT)
public class InterpolatedMulticurveBundleFnTest {
private static Tenor[] s_tenors;
static {
s_tenors = new Tenor[] {Tenor.ONE_WEEK, Tenor.ONE_MONTH, Tenor.THREE_MONTHS, Tenor.SIX_MONTHS, Tenor.ONE_YEAR,
Tenor.TWO_YEARS, Tenor.THREE_YEARS, Tenor.FIVE_YEARS, Tenor.TEN_YEARS};
}
private final class TestVersionCorrectionProvider implements VersionCorrectionProvider {
@Override
public VersionCorrection getPortfolioVersionCorrection() {
return LATEST;
}
@Override
public VersionCorrection getConfigVersionCorrection() {
return LATEST;
}
}
private static final Class<?>[] MOCK_CLASSES = {
ConfigSource.class,
ConventionSource.class,
SecuritySource.class,
CurrencyMatrix.class
};
private static final RootFinderConfiguration ROOT_FINDER_CONFIG = new RootFinderConfiguration(1e-9, 1e-9, 1000);
private DiscountingMulticurveBundleFn _multicurveBundleFn;
private Environment _environment;
private CurveConstructionConfiguration _usdDiscountingCCC;
@BeforeClass
public void init() throws IOException {
//builds graph, initializing mocks
ClassToInstanceMap<Object> mocks = MockUtils.mocks(MOCK_CLASSES);
initConfigSource(mocks.getInstance(ConfigSource.class));
initConventionSource(mocks.getInstance(ConventionSource.class));
initSecuritySource(mocks.getInstance(SecuritySource.class));
mocks.putInstance(RootFinderConfiguration.class, ROOT_FINDER_CONFIG);
mocks.putInstance(HolidaySource.class, new WeekendHolidaySource());
ComponentMap components = ComponentMap.of(mocks);
FunctionModelConfig config =
config(
arguments(
function(
DefaultDiscountingMulticurveBundleFn.class,
argument("impliedCurveNames", StringSet.of()))),
implementations(
CurveDefinitionFn.class, DefaultCurveDefinitionFn.class,
CurveSpecificationFn.class, DefaultCurveSpecificationFn.class,
CurveSpecificationMarketDataFn.class, DefaultCurveSpecificationMarketDataFn.class,
FixingsFn.class, DefaultFixingsFn.class,
FXMatrixFn.class, DefaultFXMatrixFn.class,
DiscountingMulticurveBundleFn.class, InterpolatedMulticurveBundleFn.class,
MarketDataFn.class, DefaultMarketDataFn.class));
_multicurveBundleFn = FunctionModel.build(DiscountingMulticurveBundleFn.class, config, components);
ZonedDateTime valuationDate = ZonedDateTime.of(2014, 1, 10, 11, 0, 0, 0, ZoneId.of("America/Chicago"));
MarketDataBundle marketDataBundle =
MarketDataResourcesLoader.getPreloadedBundle(
"/regression/curve_testing/usdMarketQuotes.discountFactors.properties",
"Ticker");
_environment = new SimpleEnvironment(valuationDate, marketDataBundle);
VersionCorrectionProvider vcProvider = new TestVersionCorrectionProvider();
ServiceContext serviceContext = ServiceContext.of(mocks).with(VersionCorrectionProvider.class, vcProvider);
ThreadLocalServiceContext.init(serviceContext);
_usdDiscountingCCC = createUSDCurveConstructionConfig();
}
/**
* Check that curve built from discount factor nodes has expected discount factors.
*/
@Test
public void testUSD() {
Result<MulticurveBundle> bundle = _multicurveBundleFn.generateBundle(
_environment, _usdDiscountingCCC,
ImmutableMap.<CurveConstructionConfiguration, Result<MulticurveBundle>>of());
assertTrue("Curve bundle result failed", bundle.isSuccess());
MulticurveBundle value = bundle.getValue();
MulticurveProviderDiscount multicurve = value.getMulticurveProvider();
double expectedDF = 1.0;
for (Tenor tenor : s_tenors) {
double time = TimeCalculator.getTimeBetween(_environment.getValuationTime(), _environment.getValuationTime().plus(tenor.getPeriod()));
ExternalIdBundle id = ExternalIdBundle.of("Ticker", tenor.toFormattedString().substring(1));
RawId<Double> marketDataId = RawId.of(id);
Result<Double> result = _environment.getMarketDataBundle().get(marketDataId, Double.class);
expectedDF = result.getValue();
//expectedDF = (double) _environment.getMarketDataEnvironment().get(marketDataId).getValue();
double actualDF = multicurve.getDiscountFactor(Currency.USD, time);
// simple check that market data values are being returned as discount factors
assertEquals("USD DF for tenor " + tenor + " (" + tenor.toFormattedString() + ") mismatch", expectedDF, actualDF, 10E-6);
}
// check we have flat extrapolation
double actualDF = multicurve.getDiscountFactor(Currency.USD, 10000d);
assertEquals("Right extrapolation not flat", expectedDF, actualDF, 10E-6);
}
private CurveConstructionConfiguration createUSDCurveConstructionConfig() {
List<CurveTypeConfiguration> ctc = Lists.newArrayList(new DiscountingCurveTypeConfiguration("USD"), new OvernightCurveTypeConfiguration(ExternalId.of("BLOOMBERG_TICKER", "FEDL01 Index")));
Map<String, List<? extends CurveTypeConfiguration>> ct = Maps.newHashMap();
ct.put("USD DiscountingDiscountFactorNodes", ctc);
List<CurveGroupConfiguration> cgc = Lists.newArrayList(new CurveGroupConfiguration(0, ct ));
return new CurveConstructionConfiguration("USD", cgc, Collections.<String>emptyList());
}
private void initConfigSource(ConfigSource cs) {
InterpolatedCurveDefinition usdDiscountingCurve = loadConfig(InterpolatedCurveDefinition.class, "USD_DiscountingDiscountFactorNodes.xml", false);
when(cs.get(Object.class, "USD DiscountingDiscountFactorNodes", LATEST)).thenReturn(Collections.singletonList(ConfigItem.<Object> of(usdDiscountingCurve)));
when(cs.get(AbstractCurveDefinition.class, "USD DiscountingDiscountFactorNodes", LATEST)).thenReturn(Collections.singletonList(ConfigItem.<AbstractCurveDefinition> of(usdDiscountingCurve)));
when(cs.getSingle(InterpolatedCurveDefinition.class, "USD DiscountingDiscountFactorNodes", LATEST)).thenReturn(usdDiscountingCurve);
CurveNodeIdMapper nodeIdMapper = loadConfig(CurveNodeIdMapper.class, "USD_OIS_NodeMapper.xml", true);
when(cs.getSingle(CurveNodeIdMapper.class, "USD OIS Node Mapper", LATEST)).thenReturn(nodeIdMapper);
}
private void initConventionSource(ConventionSource cs) {
SwapFixedLegConvention fixedLegConvention = loadConvention(SwapFixedLegConvention.class, "USD_OIS_Fixed_Leg.xml", false);
when(cs.getSingle(ExternalId.of("CONVENTION", "USD OIS Fixed Leg"), FinancialConvention.class)).thenReturn(fixedLegConvention);
FinancialConvention oisOvernight = loadConvention(OISLegConvention.class, "USD_OIS_Overnight_Leg.xml", false);
when(cs.getSingle(ExternalId.of("CONVENTION", "USD OIS Overnight Leg"), FinancialConvention.class)).thenReturn(oisOvernight);
OvernightIndexConvention ff = loadConvention(OvernightIndexConvention.class, "FedFundsEffectiveRateU.xml", false);
when(cs.getSingle(ExternalId.of("BLOOMBERG_CONVENTION_NAME", "Federal Funds Effective Rate U"), FinancialConvention.class)).thenReturn(ff);
when(cs.getSingle(ExternalId.of("BLOOMBERG_CONVENTION_NAME", "Federal Funds Effective Rate U"), OvernightIndexConvention.class)).thenReturn(ff);
when(cs.getSingle(ExternalId.of("BLOOMBERG_CONVENTION_NAME", "Federal Funds Effective Rate U").toBundle(), LATEST)).thenReturn(ff);
}
private void initSecuritySource(SecuritySource ss) {
Security sec = loadSecurity(Security.class, "FEDL01 Index.xml", false);
when(ss.getSingle(ExternalId.of("BLOOMBERG_TICKER", "FEDL01 Index").toBundle())).thenReturn(sec);
when(ss.getSingle(ExternalId.of("BLOOMBERG_TICKER", "FEDL01 Index").toBundle(), LATEST)).thenReturn(sec);
}
private Security loadSecurity(Class<Security> clazz, String path, boolean isFudge) {
return loadBean(clazz, "security/" + path, isFudge);
}
private <T> T loadConfig(Class<T> clazz, String path, boolean isFudge) {
return loadBean(clazz, "config/" + path, isFudge);
}
private <T> T loadConvention(Class<T> clazz, String path, boolean isFudge) {
return loadBean(clazz, "convention/" + path, isFudge);
}
private <T> T loadBean(Class<T> clazz, String path, boolean isFudge) {
try {
File file = new ClassPathResource("regression/curve_testing/" + path).getFile();
if (!isFudge) {
return JodaBeanSerialization.deserializer().xmlReader().read(new FileInputStream(file), clazz);
} else {
FudgeContext context = OpenGammaFudgeContext.getInstance();
FudgeXMLStreamReader streamReader = new FudgeXMLStreamReader(context, new FileReader(file));
// Don't close fudgeMsgReader; the caller will close the stream later
@SuppressWarnings("resource")
FudgeMsgReader fudgeMsgReader = new FudgeMsgReader(streamReader);
FudgeMsg msg = fudgeMsgReader.nextMessage();
return new FudgeDeserializer(context).fudgeMsgToObject(clazz, msg);
}
} catch (IOException ex) {
throw Throwables.propagate(ex);
}
}
}