/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.generator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.LocalDate; import org.threeten.bp.ZonedDateTime; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries; import com.opengamma.core.position.Counterparty; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.financial.analytics.ircurve.CurveSpecificationBuilderConfiguration; import com.opengamma.financial.convention.ConventionBundle; import com.opengamma.financial.convention.InMemoryConventionBundleMaster; import com.opengamma.financial.security.swap.FixedInterestRateLeg; import com.opengamma.financial.security.swap.FloatingInterestRateLeg; import com.opengamma.financial.security.swap.FloatingRateType; import com.opengamma.financial.security.swap.ForwardSwapSecurity; import com.opengamma.financial.security.swap.InterestRateNotional; import com.opengamma.financial.security.swap.SwapLeg; import com.opengamma.id.ExternalId; import com.opengamma.master.position.ManageableTrade; import com.opengamma.util.money.Currency; import com.opengamma.util.time.Tenor; /** * Source of random, but reasonable, forward swap security instances. */ public class ForwardSwapSecurityGenerator extends SecurityGenerator<ForwardSwapSecurity> { private static final Logger s_logger = LoggerFactory.getLogger(ForwardSwapSecurityGenerator.class); private static final Tenor[] TENORS = new Tenor[] {Tenor.THREE_MONTHS, Tenor.SIX_MONTHS, Tenor.NINE_MONTHS, Tenor.ONE_YEAR, Tenor.TWO_YEARS }; private static final Tenor[] FORWARDS = new Tenor[] {Tenor.THREE_MONTHS, Tenor.SIX_MONTHS, Tenor.NINE_MONTHS }; private int _daysTrading = 30; public void setDaysTrading(final int daysTrading) { _daysTrading = daysTrading; } public int getDaysTrading() { return _daysTrading; } /** * Return the time series identifier. * * @param liborConvention the convention bundle, not null * @return the time series identifier */ protected ExternalId getTimeSeriesIdentifier(final ConventionBundle liborConvention) { return liborConvention.getIdentifiers().getExternalId(getPreferredScheme()); } private ExternalId getSwapRateFor(Currency ccy, LocalDate tradeDate, Tenor maturityTenor, Tenor forwardTenor) { final CurveSpecificationBuilderConfiguration curveSpecConfig = getCurrencyCurveConfig(ccy); if (curveSpecConfig == null) { return null; } final ExternalId swapSecurity; final Tenor tenor; final int months = (int) maturityTenor.getPeriod().toTotalMonths() + (int) forwardTenor.getPeriod().toTotalMonths(); if (months < 12) { tenor = Tenor.ofMonths(months); } else { // TODO: this isn't particularly great tenor = Tenor.ofYears(months / 12); } try { if (ccy.equals(Currency.USD)) { // Standard (i.e. matches convention) floating leg tenor for USD is 3M swapSecurity = curveSpecConfig.getSwap3MSecurity(tradeDate, tenor); } else { // Standard (i.e. matches convention) floating leg tenor for CHF, JPY, GBP, EUR is 6M swapSecurity = curveSpecConfig.getSwap6MSecurity(tradeDate, tenor); } } catch (OpenGammaRuntimeException e) { return null; } return swapSecurity; } @Override public ForwardSwapSecurity createSecurity() { final Currency ccy = getRandomCurrency(); final ZonedDateTime now = ZonedDateTime.now(); final ZonedDateTime tradeDate = previousWorkingDay(now.minusDays(getRandom(getDaysTrading())), ccy); final Tenor forward = getRandom(FORWARDS); final ZonedDateTime forwardDate = nextWorkingDay(now.plus(forward.getPeriod()), ccy); ConventionBundle swapConvention = getConventionBundleSource().getConventionBundle(ExternalId.of(InMemoryConventionBundleMaster.SIMPLE_NAME_SCHEME, ccy.getCode() + "_SWAP")); if (swapConvention == null) { s_logger.error("Couldn't get swap convention for {}", ccy.getCode()); return null; } final Tenor maturity = getRandom(TENORS); // get the convention of the identifier of the initial rate ConventionBundle liborConvention = getConventionBundleSource().getConventionBundle(swapConvention.getSwapFloatingLegInitialRate()); if (liborConvention == null) { s_logger.error("Couldn't get libor convention for {}", swapConvention.getSwapFloatingLegInitialRate()); return null; } // look up the rate timeseries identifier out of the bundle final ExternalId tsIdentifier = getTimeSeriesIdentifier(liborConvention); // look up the value on our chosen trade date final HistoricalTimeSeries initialRateSeries = getHistoricalSource().getHistoricalTimeSeries(MarketDataRequirementNames.MARKET_VALUE, tsIdentifier.toBundle(), null, tradeDate.toLocalDate(), true, tradeDate.toLocalDate(), true); if (initialRateSeries == null || initialRateSeries.getTimeSeries().isEmpty()) { s_logger.error("couldn't get series for {} on {}", tsIdentifier, tradeDate); return null; } Double initialRate = initialRateSeries.getTimeSeries().getEarliestValue(); // get the identifier for the swap rate for the maturity we're interested in (assuming the fixed rate will be =~ swap rate) final ExternalId swapRateForMaturityIdentifier = getSwapRateFor(ccy, tradeDate.toLocalDate(), maturity, forward); if (swapRateForMaturityIdentifier == null) { s_logger.error("Couldn't get swap rate identifier for {} [{}] from {}", new Object[] {ccy, maturity, tradeDate }); return null; } final HistoricalTimeSeries fixedRateSeries = getHistoricalSource().getHistoricalTimeSeries(MarketDataRequirementNames.MARKET_VALUE, swapRateForMaturityIdentifier.toBundle(), null, tradeDate.toLocalDate(), true, tradeDate.toLocalDate(), true); if (fixedRateSeries == null) { s_logger.error("can't find time series for {} on {}", swapRateForMaturityIdentifier, tradeDate); return null; } Double fixedRate = (fixedRateSeries.getTimeSeries().getEarliestValue() + getRandom().nextDouble()) / 100d; Double notional = (double) getRandom(100000) * 1000; ZonedDateTime maturityDate = forwardDate.plus(maturity.getPeriod()); String counterparty = "CParty"; SwapLeg fixedLeg = new FixedInterestRateLeg(swapConvention.getSwapFixedLegDayCount(), swapConvention.getSwapFixedLegFrequency(), swapConvention.getSwapFixedLegRegion(), swapConvention.getSwapFixedLegBusinessDayConvention(), new InterestRateNotional(ccy, notional), false, fixedRate); FloatingInterestRateLeg floatingLeg = new FloatingInterestRateLeg(swapConvention.getSwapFloatingLegDayCount(), swapConvention.getSwapFloatingLegFrequency(), swapConvention.getSwapFloatingLegRegion(), swapConvention.getSwapFloatingLegBusinessDayConvention(), new InterestRateNotional(ccy, notional), false, tsIdentifier, FloatingRateType.IBOR); floatingLeg.setInitialFloatingRate(initialRate); String fixedLegDescription = RATE_FORMATTER.format(fixedRate); String floatingLegDescription = swapConvention.getSwapFloatingLegInitialRate().getValue(); boolean isPayFixed = getRandom().nextBoolean(); SwapLeg payLeg; String payLegDescription; SwapLeg receiveLeg; String receiveLegDescription; if (isPayFixed) { payLeg = fixedLeg; payLegDescription = fixedLegDescription; receiveLeg = floatingLeg; receiveLegDescription = floatingLegDescription; } else { payLeg = floatingLeg; payLegDescription = floatingLegDescription; receiveLeg = fixedLeg; receiveLegDescription = fixedLegDescription; } final ForwardSwapSecurity swap = new ForwardSwapSecurity(tradeDate, tradeDate, maturityDate, counterparty, payLeg, receiveLeg, forwardDate); swap.setName("IR Forward Swap " + ccy + " " + NOTIONAL_FORMATTER.format(notional) + " " + maturity.getPeriod() + " from " + forwardDate.toString(DATE_FORMATTER) + " - " + payLegDescription + " / " + receiveLegDescription); return swap; } @Override public ManageableTrade createSecurityTrade(final QuantityGenerator quantity, final SecurityPersister persister, final NameGenerator counterPartyGenerator) { final ForwardSwapSecurity swap = createSecurity(); if (swap != null) { return new ManageableTrade(quantity.createQuantity(), persister.storeSecurity(swap), swap.getTradeDate().toLocalDate(), swap.getTradeDate().toOffsetDateTime().toOffsetTime(), ExternalId.of(Counterparty.DEFAULT_SCHEME, counterPartyGenerator.createName())); } else { return null; } } }