/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.fxrates;
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.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.testng.Assert.assertNull;
import java.io.IOException;
import java.math.BigDecimal;
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.OffsetTime;
import org.threeten.bp.Period;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.legalentity.SeniorityLevel;
import com.opengamma.core.link.ConfigLink;
import com.opengamma.core.position.Counterparty;
import com.opengamma.core.position.Trade;
import com.opengamma.core.position.impl.SimpleCounterparty;
import com.opengamma.core.position.impl.SimpleTrade;
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.convention.frequency.SimpleFrequency;
import com.opengamma.financial.currency.CurrencyMatrix;
import com.opengamma.financial.currency.CurrencyPair;
import com.opengamma.financial.currency.SimpleCurrencyMatrix;
import com.opengamma.financial.security.credit.StandardCDSSecurity;
import com.opengamma.financial.security.fra.ForwardRateAgreementSecurity;
import com.opengamma.financial.security.fx.FXForwardSecurity;
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.financial.security.swap.InterestRateNotional;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.service.ServiceContext;
import com.opengamma.service.ThreadLocalServiceContext;
import com.opengamma.service.VersionCorrectionProvider;
import com.opengamma.sesame.Environment;
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.marketdata.EmptyMarketDataFactory;
import com.opengamma.sesame.marketdata.EmptyMarketDataSpec;
import com.opengamma.sesame.marketdata.FxRateId;
import com.opengamma.sesame.marketdata.MarketDataEnvironment;
import com.opengamma.sesame.marketdata.MarketDataEnvironmentBuilder;
import com.opengamma.sesame.marketdata.builders.FxRateMarketDataBuilder;
import com.opengamma.sesame.marketdata.builders.MarketDataEnvironmentFactory;
import com.opengamma.sesame.trade.FXForwardTrade;
import com.opengamma.util.GUIDGenerator;
import com.opengamma.util.function.Function;
import com.opengamma.util.money.Currency;
import com.opengamma.util.result.Result;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.time.DateUtils;
/**
* Tests fx rates for base currency against various trades.
*/
@Test(groups = TestGroup.UNIT)
public class FxRatesTest {
private static CurrencyPair EUR_USD = CurrencyPair.of(Currency.EUR, Currency.USD);
private static CurrencyPair EUR_GBP = CurrencyPair.of(Currency.EUR, Currency.GBP);
private static CurrencyPair EUR_AUD = CurrencyPair.of(Currency.EUR, Currency.AUD);
private static double EUR_USD_RATE = 1.09;
private static double EUR_GBP_RATE = 0.72;
private static double EUR_AUD_RATE = 1.39;
private static double EUR_EUR_RATE = 1.0;
private static final ZonedDateTime VALUATION_TIME = DateUtils.getUTCDate(2014, 1, 22);
private static final MarketDataEnvironment ENV = getMarketDataEnvironment(VALUATION_TIME);
private static final CalculationArguments ARGS =
CalculationArguments.builder()
.valuationTime(VALUATION_TIME)
.marketDataSpecification(EmptyMarketDataSpec.INSTANCE)
.build();
private FxRatesFn _function;
private FunctionRunner _functionRunner;
@BeforeClass
public void setUpClass() throws IOException {
FunctionModelConfig engineFunctionConfig =
config(
arguments(
function(
DefaultFxRatesFn.class,
argument("baseCurrency", Currency.EUR))
),
implementations(
FxRatesFn.class, DefaultFxRatesFn.class));
SimpleCurrencyMatrix matrix = new SimpleCurrencyMatrix();
FxRateMarketDataBuilder fxBuilder = new FxRateMarketDataBuilder(ConfigLink.<CurrencyMatrix>resolved(matrix));
ComponentMap map = ComponentMap.EMPTY;
VersionCorrectionProvider vcProvider = new FixedInstantVersionCorrectionProvider(Instant.now());
ServiceContext serviceContext = ServiceContext.of(map.getComponents()).with(VersionCorrectionProvider.class, vcProvider);
ThreadLocalServiceContext.init(serviceContext);
EmptyMarketDataFactory dataFactory = new EmptyMarketDataFactory();
MarketDataEnvironmentFactory environmentFactory = new MarketDataEnvironmentFactory(dataFactory, fxBuilder);
_functionRunner = new FunctionRunner(environmentFactory);
_function = FunctionModel.build(FxRatesFn.class, engineFunctionConfig, map);
}
public static MarketDataEnvironment getMarketDataEnvironment(ZonedDateTime valuation) {
MarketDataEnvironmentBuilder builder = new MarketDataEnvironmentBuilder();
FxRateId eudUsd = FxRateId.of(EUR_USD);
builder.add(eudUsd, EUR_USD_RATE);
FxRateId eurGbp = FxRateId.of(EUR_GBP);
builder.add(eurGbp, EUR_GBP_RATE);
FxRateId eurAud = FxRateId.of(EUR_AUD);
builder.add(eurAud, EUR_AUD_RATE);
builder.valuationTime(valuation);
return builder.build();
}
private static final InterestRateSwapNotional USD_NOTIONAL = new InterestRateSwapNotional(Currency.USD, 100_000_000);
private static final InterestRateSwapNotional GBP_NOTIONAL = new InterestRateSwapNotional(Currency.GBP, 61_600_000);
private static final PeriodFrequency P6M = PeriodFrequency.of(Period.ofMonths(6));
private static final PeriodFrequency P3M = PeriodFrequency.of(Period.ofMonths(3));
private static final Set<ExternalId> USNY = Sets.newHashSet(ExternalId.of(ExternalSchemes.ISDA_HOLIDAY, "USNY"));
private static final Set<ExternalId> GBLO = Sets.newHashSet(ExternalId.of(ExternalSchemes.ISDA_HOLIDAY, "GBLO"));
private static InterestRateSwapSecurity createUsdGbpSwap() {
FixedInterestRateSwapLeg payLeg = new FixedInterestRateSwapLeg();
payLeg.setNotional(USD_NOTIONAL);
payLeg.setDayCountConvention(DayCounts.THIRTY_U_360);
payLeg.setPaymentDateFrequency(P6M);
payLeg.setPaymentDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING);
payLeg.setPaymentDateCalendars(USNY);
payLeg.setAccrualPeriodFrequency(P6M);
payLeg.setAccrualPeriodBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING);
payLeg.setAccrualPeriodCalendars(USNY);
payLeg.setMaturityDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING);
payLeg.setMaturityDateCalendars(USNY);
payLeg.setRate(new Rate(0.03));
payLeg.setPayReceiveType(PayReceiveType.PAY);
FloatingInterestRateSwapLeg receiveLeg = new FloatingInterestRateSwapLeg();
receiveLeg.setNotional(GBP_NOTIONAL);
receiveLeg.setDayCountConvention(DayCounts.ACT_365);
receiveLeg.setPaymentDateFrequency(P3M);
receiveLeg.setPaymentDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING);
receiveLeg.setPaymentDateCalendars(GBLO);
receiveLeg.setAccrualPeriodFrequency(P3M);
receiveLeg.setAccrualPeriodBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING);
receiveLeg.setAccrualPeriodCalendars(GBLO);
receiveLeg.setResetPeriodFrequency(P3M);
receiveLeg.setResetPeriodBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING);
receiveLeg.setResetPeriodCalendars(GBLO);
receiveLeg.setFixingDateBusinessDayConvention(BusinessDayConventions.PRECEDING);
receiveLeg.setMaturityDateBusinessDayConvention(BusinessDayConventions.MODIFIED_FOLLOWING);
receiveLeg.setMaturityDateCalendars(GBLO);
receiveLeg.setFixingDateCalendars(GBLO);
receiveLeg.setFixingDateOffset(0);
receiveLeg.setFloatingRateType(FloatingRateType.IBOR);
receiveLeg.setFloatingReferenceRateId(ExternalId.of("BLOOMBERG_TICKER", "BP0003M Index"));
receiveLeg.setPayReceiveType(PayReceiveType.RECEIVE);
List<InterestRateSwapLeg> legs = ImmutableList.<InterestRateSwapLeg>of(payLeg, receiveLeg);
return new InterestRateSwapSecurity(
ExternalIdBundle.of(ExternalId.of("UUID", GUIDGenerator.generate().toString())),
"XCCY - US Fixed vs Libor BP 3m",
LocalDate.of(2014, 1, 24), // effective date
LocalDate.of(2021, 1, 24), // maturity date,
legs);
}
private static ForwardRateAgreementSecurity createAudFra() {
return new ForwardRateAgreementSecurity(
Currency.AUD,
ExternalId.of("BLOOMBERG_TICKER", "AU0003M"),
SimpleFrequency.QUARTERLY,
LocalDate.of(2014, 9, 12), // start date
LocalDate.of(2014, 12, 12), // end date
0.0125,
-10000000,
DayCounts.ACT_360,
BusinessDayConventions.MODIFIED_FOLLOWING,
Sets.newHashSet(ExternalId.of(ExternalSchemes.ISDA_HOLIDAY, "AUD")),
Sets.newHashSet(ExternalId.of(ExternalSchemes.ISDA_HOLIDAY, "AUD")),
2);
}
private static StandardCDSSecurity createEurCds() {
return new StandardCDSSecurity(ExternalIdBundle.of("Sample", "Bundle"),
"Standard CDS",
LocalDate.of(2014, 9, 20),
LocalDate.of(2019, 12, 20),
ExternalId.of("Sample", "Id"),
new InterestRateNotional(Currency.EUR, 10_000_000),
true,
0.01,
SeniorityLevel.SNRFOR);
}
private static FXForwardSecurity createGbpZarFxForward(){
return new FXForwardSecurity(Currency.GBP,
1_000_000,
Currency.ZAR,
16_000_000,
DateUtils.getUTCDate(2019, 2, 4),
ExternalSchemes.currencyRegionId(Currency.GBP));
}
private static FXForwardSecurity createGbpEurFxForward(){
return new FXForwardSecurity(Currency.GBP,
1_000_000,
Currency.EUR,
1_600_000,
DateUtils.getUTCDate(2019, 2, 4),
ExternalSchemes.currencyRegionId(Currency.GBP));
}
private static FXForwardTrade createGbpEurFxForwardTrade(){
FXForwardSecurity security = new FXForwardSecurity(Currency.GBP,
1_000_000,
Currency.EUR,
1_600_000,
DateUtils.getUTCDate(2019, 2, 4),
ExternalSchemes.currencyRegionId(Currency.GBP));
Trade trade = new SimpleTrade(security,
BigDecimal.ONE,
new SimpleCounterparty(ExternalId.of(Counterparty.DEFAULT_SCHEME, "CPARTY")),
LocalDate.now(),
OffsetTime.now());
return new FXForwardTrade(trade);
}
@Test
public void testUsdGbpSwap() {
Result<Map<Currency, Double>> result =
_functionRunner.runFunction(ARGS, ENV, new Function<Environment, Result<Map<Currency, Double>>>() {
@Override
public Result<Map<Currency, Double>> apply(Environment env) {
return _function.getFxRates(env, createUsdGbpSwap());
}
});
assertThat(result.isSuccess(), is(true));
Map<Currency, Double> rates = result.getValue();
assertThat(rates.size(), is(2));
assertThat(rates.get(Currency.USD), is(EUR_USD_RATE));
assertThat(rates.get(Currency.GBP), is(EUR_GBP_RATE));
assertNull(rates.get(Currency.AUD));
}
@Test
public void testAudFra() {
Result<Map<Currency, Double>> result =
_functionRunner.runFunction(ARGS, ENV, new Function<Environment, Result<Map<Currency, Double>>>() {
@Override
public Result<Map<Currency, Double>> apply(Environment env) {
return _function.getFxRates(env, createAudFra());
}
});
assertThat(result.isSuccess(), is(true));
Map<Currency, Double> rates = result.getValue();
assertThat(rates.size(), is(1));
assertThat(rates.get(Currency.AUD), is(EUR_AUD_RATE));
assertNull(rates.get(Currency.USD));
assertNull(rates.get(Currency.GBP));
}
@Test
public void testGbpZarFxForward() {
Result<Map<Currency, Double>> result =
_functionRunner.runFunction(ARGS, ENV, new Function<Environment, Result<Map<Currency, Double>>>() {
@Override
public Result<Map<Currency, Double>> apply(Environment env) {
return _function.getFxRates(env, createGbpZarFxForward());
}
});
// No ZAR values exist
assertThat(result.isSuccess(), is(false));
}
@Test
public void testGbpEurFxForward() {
Result<Map<Currency, Double>> result =
_functionRunner.runFunction(ARGS, ENV, new Function<Environment, Result<Map<Currency, Double>>>() {
@Override
public Result<Map<Currency, Double>> apply(Environment env) {
return _function.getFxRates(env, createGbpEurFxForward());
}
});
assertThat(result.isSuccess(), is(true));
Map<Currency, Double> rates = result.getValue();
assertThat(rates.size(), is(2));
assertThat(rates.get(Currency.GBP), is(EUR_GBP_RATE));
assertThat(rates.get(Currency.EUR), is(EUR_EUR_RATE));
}
@Test
public void testGbpEurFxForwardTrade() {
Result<Map<Currency, Double>> result =
_functionRunner.runFunction(ARGS, ENV, new Function<Environment, Result<Map<Currency, Double>>>() {
@Override
public Result<Map<Currency, Double>> apply(Environment env) {
return _function.getFxRates(env, createGbpEurFxForwardTrade());
}
});
assertThat(result.isSuccess(), is(true));
Map<Currency, Double> rates = result.getValue();
assertThat(rates.size(), is(2));
assertThat(rates.get(Currency.GBP), is(EUR_GBP_RATE));
assertThat(rates.get(Currency.EUR), is(EUR_EUR_RATE));
}
@Test
public void testEurCds() {
Result<Map<Currency, Double>> result =
_functionRunner.runFunction(ARGS, ENV, new Function<Environment, Result<Map<Currency, Double>>>() {
@Override
public Result<Map<Currency, Double>> apply(Environment env) {
return _function.getFxRates(env, createEurCds());
}
});
assertThat(result.isSuccess(), is(true));
Map<Currency, Double> rates = result.getValue();
assertThat(rates.size(), is(1));
assertThat(rates.get(Currency.EUR), is(EUR_EUR_RATE));
}
}