package com.opengamma.sesame.deliverableswapfuture; 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.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.threeten.bp.Instant; import org.threeten.bp.LocalDate; import org.threeten.bp.LocalDateTime; 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.Sets; import com.opengamma.core.change.ChangeManager; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource; import com.opengamma.core.historicaltimeseries.impl.SimpleHistoricalTimeSeries; 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.core.value.MarketDataRequirementNames; 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.convention.businessday.BusinessDayConvention; import com.opengamma.financial.convention.businessday.BusinessDayConventions; import com.opengamma.financial.convention.daycount.DayCounts; import com.opengamma.financial.convention.frequency.PeriodFrequency; import com.opengamma.financial.security.future.DeliverableSwapFutureSecurity; import com.opengamma.financial.security.irs.FixedInterestRateSwapLeg; import com.opengamma.financial.security.irs.FloatingInterestRateSwapLeg; import com.opengamma.financial.security.irs.InterestRateSwapLeg; import com.opengamma.financial.security.irs.InterestRateSwapNotional; import com.opengamma.financial.security.irs.InterestRateSwapSecurity; import com.opengamma.financial.security.irs.PayReceiveType; import com.opengamma.financial.security.irs.Rate; import com.opengamma.financial.security.swap.FloatingRateType; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.id.UniqueId; 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.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.DefaultDiscountingMulticurveBundleFn; import com.opengamma.sesame.DefaultDiscountingMulticurveBundleResolverFn; import com.opengamma.sesame.DefaultFXMatrixFn; import com.opengamma.sesame.DefaultFixingsFn; import com.opengamma.sesame.DiscountingMulticurveBundleFn; import com.opengamma.sesame.DiscountingMulticurveBundleResolverFn; 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.SimpleEnvironment; import com.opengamma.sesame.component.RetrievalPeriod; import com.opengamma.sesame.component.StringSet; import com.opengamma.sesame.config.FunctionModelConfig; import com.opengamma.sesame.engine.ComponentMap; import com.opengamma.sesame.engine.FixedInstantVersionCorrectionProvider; 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.MapMarketDataBundle; import com.opengamma.sesame.marketdata.MarketDataBundle; import com.opengamma.sesame.marketdata.MarketDataEnvironmentBuilder; import com.opengamma.sesame.marketdata.MarketDataFn; import com.opengamma.sesame.marketdata.RawId; import com.opengamma.sesame.trade.DeliverableSwapFutureTrade; import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries; import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries; import com.opengamma.util.money.Currency; 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 the deliverable swap future analytic functions using the discounting calculator. */ @Test(groups = TestGroup.UNIT) public class DeliverableSwapFutureFnTest { private static final ZonedDateTime VALUATION_TIME = DateUtils.getUTCDate(2014, 1, 22); public static final LocalDate TRADE_DATE = LocalDate.of(2014, 6, 1).minusDays(2000); private DeliverableSwapFutureFn _deliverableSwapFutureFn; private static final InterestRateSwapSecurity UNDERLYING_SWAP = createUnderlyingSwap(); private static final DeliverableSwapFutureTrade TRADE = createDeliverableSwapFutureTrade(); private static final Environment ENV = new SimpleEnvironment(VALUATION_TIME, createMarketDataBundle()); private static MarketDataBundle createMarketDataBundle() { LocalDate dataDate = LocalDate.of(2014, 2, 18); MarketDataEnvironmentBuilder builder = InterestRateMockSources.createMarketDataEnvironment(dataDate).toBuilder(); LocalDate valuationDate = VALUATION_TIME.toLocalDate(); LocalDateDoubleTimeSeries priceSeries = ImmutableLocalDateDoubleTimeSeries.of(valuationDate, 0.975); RawId<Double> id = RawId.of(TRADE.getTrade().getSecurity().getExternalIdBundle()); builder.add(id, priceSeries); return new MapMarketDataBundle(builder.build()); } @BeforeClass public void setUpClass() throws IOException { 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( DefaultDiscountingMulticurveBundleFn.class, argument("impliedCurveNames", StringSet.of())), function( DefaultCurveNodeConverterFn.class, argument("timeSeriesDuration", RetrievalPeriod.of(Period.ofYears(1))))), implementations( DeliverableSwapFutureFn.class, DefaultDeliverableSwapFutureFn.class, DeliverableSwapFutureCalculatorFactory.class, DeliverableSwapFutureDiscountingCalculatorFactory.class, CurveSpecificationMarketDataFn.class, DefaultCurveSpecificationMarketDataFn.class, DiscountingMulticurveBundleResolverFn.class, DefaultDiscountingMulticurveBundleResolverFn.class, CurveNodeConverterFn.class, DefaultCurveNodeConverterFn.class, FXMatrixFn.class, DefaultFXMatrixFn.class, DiscountingMulticurveCombinerFn.class, ExposureFunctionsDiscountingMulticurveCombinerFn.class, CurveDefinitionFn.class, DefaultCurveDefinitionFn.class, CurveLabellingFn.class, CurveDefinitionCurveLabellingFn.class, DiscountingMulticurveBundleFn.class, DefaultDiscountingMulticurveBundleFn.class, CurveSpecificationFn.class, DefaultCurveSpecificationFn.class, CurveConstructionConfigurationSource.class, ConfigDBCurveConstructionConfigurationSource.class, HistoricalMarketDataFn.class, DefaultHistoricalMarketDataFn.class, FixingsFn.class, DefaultFixingsFn.class, MarketDataFn.class, DefaultMarketDataFn.class)); ImmutableMap<Class<?>, Object> components = generateComponents(); VersionCorrectionProvider vcProvider = new FixedInstantVersionCorrectionProvider(Instant.now()); ServiceContext serviceContext = ServiceContext.of(components).with(VersionCorrectionProvider.class, vcProvider); ThreadLocalServiceContext.init(serviceContext); _deliverableSwapFutureFn = FunctionModel.build(DeliverableSwapFutureFn.class, config, ComponentMap.of(components)); } private static InterestRateSwapSecurity createUnderlyingSwap() { InterestRateSwapNotional notional = new InterestRateSwapNotional(Currency.USD, 1); Rate rate = new Rate(0.01); BusinessDayConvention rollConv = BusinessDayConventions.MODIFIED_FOLLOWING; Set<ExternalId> calendarUSNY = Sets.newHashSet(ExternalId.of(ExternalSchemes.ISDA_HOLIDAY, "USNY")); FixedInterestRateSwapLeg fixedLeg = new FixedInterestRateSwapLeg(); fixedLeg.setNotional(notional); fixedLeg.setRate(rate); fixedLeg.setAccrualPeriodBusinessDayConvention(rollConv); fixedLeg.setAccrualPeriodFrequency(PeriodFrequency.SEMI_ANNUAL); fixedLeg.setAccrualPeriodCalendars(calendarUSNY); fixedLeg.setPaymentDateBusinessDayConvention(rollConv); fixedLeg.setPaymentDateFrequency(PeriodFrequency.SEMI_ANNUAL); fixedLeg.setPaymentDateCalendars(calendarUSNY); fixedLeg.setPayReceiveType(PayReceiveType.RECEIVE); fixedLeg.setDayCountConvention(DayCounts.THIRTY_360); FloatingInterestRateSwapLeg floatLeg = new FloatingInterestRateSwapLeg(); floatLeg.setNotional(notional); floatLeg.setFloatingReferenceRateId(InterestRateMockSources.getLiborIndexId()); floatLeg.setFloatingRateType(FloatingRateType.IBOR); floatLeg.setDayCountConvention(DayCounts.ACT_360); floatLeg.setResetPeriodBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING); floatLeg.setResetPeriodCalendars(calendarUSNY); floatLeg.setResetPeriodFrequency(PeriodFrequency.QUARTERLY); floatLeg.setAccrualPeriodBusinessDayConvention(rollConv); floatLeg.setAccrualPeriodFrequency(PeriodFrequency.QUARTERLY); floatLeg.setAccrualPeriodCalendars(calendarUSNY); floatLeg.setPaymentDateBusinessDayConvention(rollConv); floatLeg.setPaymentDateFrequency(PeriodFrequency.QUARTERLY); floatLeg.setPaymentDateCalendars(calendarUSNY); floatLeg.setFixingDateBusinessDayConvention(BusinessDayConventions.PRECEDING); floatLeg.setFixingDateCalendars(calendarUSNY); floatLeg.setFixingDateOffset(-2); PayReceiveType floatPayersOrRec = fixedLeg.getPayReceiveType() == PayReceiveType.PAY ? PayReceiveType.RECEIVE : PayReceiveType.PAY; floatLeg.setPayReceiveType(floatPayersOrRec); List<InterestRateSwapLeg> legs = new ArrayList<>(); legs.add(fixedLeg); legs.add(floatLeg); String swapId = Currency.USD + "_UnderlyingSwap"; ExternalIdBundle externalSwapBundleId = ExternalSchemes.syntheticSecurityId(swapId).toBundle(); return new InterestRateSwapSecurity(externalSwapBundleId, swapId, LocalDate.of(2014, 6, 18), LocalDate.of(2016, 6, 18), legs); } private static DeliverableSwapFutureTrade createDeliverableSwapFutureTrade() { Expiry expiry = new Expiry(ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2014, 6, 15), LocalTime.of(0, 0)), ZoneOffset.UTC)); DeliverableSwapFutureSecurity dsf = new DeliverableSwapFutureSecurity(expiry, Currency.USD + "DSF", Currency.USD + "DSF", Currency.USD, 1000, "DSF", UNDERLYING_SWAP.getExternalIdBundle(). getExternalIds().first(), 1); String dsfId = Currency.USD + "_TestDSF"; ExternalIdBundle externalDsfBundle = ExternalSchemes.syntheticSecurityId(dsfId).toBundle(); dsf.setName(dsfId); dsf.setExternalIdBundle(externalDsfBundle); Counterparty counterparty = new SimpleCounterparty(ExternalId.of(Counterparty.DEFAULT_SCHEME, "COUNTERPARTY")); OffsetTime tradeTime = OffsetTime.of(LocalTime.of(0, 0), ZoneOffset.UTC); BigDecimal tradeQuantity = BigDecimal.valueOf(1); SimpleTrade trade = new SimpleTrade(dsf, tradeQuantity, counterparty , TRADE_DATE, tradeTime); trade.setPremium(0.0); return new DeliverableSwapFutureTrade(trade); } private ImmutableMap<Class<?>, Object> generateComponents() { ImmutableMap.Builder<Class<?>, Object> builder = ImmutableMap.builder(); for (Map.Entry<Class<?>, Object> keys: InterestRateMockSources.generateBaseComponents().entrySet()) { if (!keys.getKey().equals(HistoricalTimeSeriesSource.class)) { builder.put(keys.getKey(), keys.getValue()); } if (keys.getKey().equals(SecuritySource.class)) { appendSecuritySourceMock((SecuritySource) keys.getValue()); } } builder.put(HistoricalTimeSeriesSource.class, mockHistoricalTimeSeriesSource()); return builder.build(); } private HistoricalTimeSeriesSource mockHistoricalTimeSeriesSource() { HistoricalTimeSeriesSource mock = mock(HistoricalTimeSeriesSource.class); when(mock.changeManager()).thenReturn(mock(ChangeManager.class)); HistoricalTimeSeries deliverableSwapFuturePrices = new SimpleHistoricalTimeSeries(UniqueId.of("Blah", "1"), // single point on valuation date but not on trade date (trade date will take trade price as last margin) ImmutableLocalDateDoubleTimeSeries.of(VALUATION_TIME.toLocalDate(), 0.975)); when(mock.getHistoricalTimeSeries(eq(MarketDataRequirementNames.MARKET_VALUE), any(ExternalIdBundle.class), eq("DEFAULT_TSS"), any(LocalDate.class), eq(true), any(LocalDate.class), eq(true))).thenReturn(deliverableSwapFuturePrices); return mock; } private void appendSecuritySourceMock(SecuritySource mock) { SecurityMaster master = ((MasterSecuritySource) mock).getMaster(); master.add(new SecurityDocument(UNDERLYING_SWAP)); } @Test public void testPresentValue() { Result<Double> pvComputed = _deliverableSwapFutureFn.calculateSecurityModelPrice(ENV, TRADE); assertSuccess(pvComputed); } @Test public void testPresentValueTradeDate() { Environment env = ENV.withValuationTimeAndFixedMarketData(TRADE_DATE.atTime(12, 0).atZone(ZoneOffset.UTC)); Result<Double> pvComputed = _deliverableSwapFutureFn.calculateSecurityModelPrice(env, TRADE); assertSuccess(pvComputed); } @Test public void testBucketedZeroDelta() { Result<BucketedCurveSensitivities> bucketedZeroDelta = _deliverableSwapFutureFn.calculateBucketedZeroIRDelta(ENV, TRADE); assertSuccess(bucketedZeroDelta); } }