/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.examples.bloomberg.loader;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.component.tool.AbstractTool;
import com.opengamma.core.config.ConfigSource;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.financial.analytics.ircurve.CurveSpecificationBuilderConfiguration;
import com.opengamma.financial.convention.businessday.BusinessDayConvention;
import com.opengamma.financial.convention.businessday.BusinessDayConventionFactory;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.financial.convention.daycount.DayCountFactory;
import com.opengamma.financial.convention.frequency.PeriodFrequency;
import com.opengamma.financial.security.FinancialSecurity;
import com.opengamma.financial.security.capfloor.CapFloorCMSSpreadSecurity;
import com.opengamma.financial.security.capfloor.CapFloorSecurity;
import com.opengamma.financial.security.swap.FloatingInterestRateLeg;
import com.opengamma.financial.security.swap.FloatingRateType;
import com.opengamma.financial.security.swap.InterestRateNotional;
import com.opengamma.financial.security.swap.SwapSecurity;
import com.opengamma.id.ExternalId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.integration.tool.IntegrationToolContext;
import com.opengamma.master.config.impl.MasterConfigSource;
import com.opengamma.master.historicaltimeseries.impl.HistoricalTimeSeriesRatingFieldNames;
import com.opengamma.master.portfolio.ManageablePortfolio;
import com.opengamma.master.portfolio.ManageablePortfolioNode;
import com.opengamma.master.portfolio.PortfolioDocument;
import com.opengamma.master.portfolio.PortfolioMaster;
import com.opengamma.master.position.ManageablePosition;
import com.opengamma.master.position.PositionDocument;
import com.opengamma.master.position.PositionMaster;
import com.opengamma.master.security.SecurityDocument;
import com.opengamma.master.security.SecurityMaster;
import com.opengamma.util.GUIDGenerator;
import com.opengamma.util.money.Currency;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Tenor;
/**
* Example code to load a simple portfolio of cap/floors and constant-maturity swaps.
* <p>
* This code is kept deliberately as simple as possible.
* There are no checks for the securities or portfolios already existing, so if you run it
* more than once you will get multiple copies portfolios and securities with the same names.
* It is designed to run against the HSQLDB example database.
*/
public class ExampleMixedCMCapFloorPortfolioLoader extends AbstractTool<IntegrationToolContext> {
/** The logger */
private static final Logger s_logger = LoggerFactory.getLogger(ExampleMixedCMCapFloorPortfolioLoader.class);
/** The trade date */
private static final LocalDate TRADE_DATE = DateUtils.previousWeekDay().minusDays(30);
/** Following business day convention */
private static final BusinessDayConvention FOLLOWING = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Following");
/** The region */
private static final ExternalId REGION = ExternalSchemes.financialRegionId("US+GB");
/** Act/360 day-count */
private static final DayCount ACT_360 = DayCountFactory.INSTANCE.getDayCount("Actual/360");
/** The currency */
private static final Currency CURRENCY = Currency.USD;
/** The counterparty */
private static final String COUNTERPARTY = "Cpty";
/** 3m Libor ticker */
private static final ExternalId LIBOR_3M = ExternalSchemes.bloombergTickerSecurityId("US0003M Index");
/** The tenors */
private static final Tenor[] TENORS = new Tenor[] {Tenor.ONE_YEAR, Tenor.TWO_YEARS, Tenor.THREE_YEARS, Tenor.FIVE_YEARS, Tenor.TEN_YEARS};
/** The pay tenors */
private static final Tenor[] PAY_TENORS = new Tenor[] {Tenor.ONE_YEAR, Tenor.FIVE_YEARS};
/** The receive tenors */
private static final Tenor[] RECEIVE_TENORS = new Tenor[] {Tenor.TWO_YEARS, Tenor.TEN_YEARS};
/** The strike formatter */
private static final DecimalFormat FORMAT = new DecimalFormat("##.##");
/** The id scheme for these securities */
private static final String ID_SCHEME = "CAP_FLOOR_SWAP_GENERATOR";
/** The portfolio name */
public static final String PORTFOLIO_NAME = "CMS and Cap/Floor Portfolio";
/** A map of tenors to swap rate tickers */
private static final Map<Tenor, ExternalId> TICKERS = new HashMap<>();
static {
TICKERS.put(Tenor.ONE_YEAR, ExternalSchemes.bloombergTickerSecurityId("USSW1 Curncy"));
TICKERS.put(Tenor.TWO_YEARS, ExternalSchemes.bloombergTickerSecurityId("USSW2 Curncy"));
TICKERS.put(Tenor.THREE_YEARS, ExternalSchemes.bloombergTickerSecurityId("USSW3 Curncy"));
TICKERS.put(Tenor.FIVE_YEARS, ExternalSchemes.bloombergTickerSecurityId("USSW5 Curncy"));
TICKERS.put(Tenor.TEN_YEARS, ExternalSchemes.bloombergTickerSecurityId("USSW10 Curncy"));
}
public static void main(final String[] args) { //CSIGNORE
new ExampleTimeSeriesRatingLoader().initAndRun(args, IntegrationToolContext.class);
new ExampleMixedCMCapFloorPortfolioLoader().initAndRun(args, IntegrationToolContext.class);
System.exit(0);
}
@Override
protected void doRun() {
final Random random = new Random(45689);
final Collection<FinancialSecurity> securities = createRandomSwaps(random, 40);
securities.addAll(createRandomCapFloors(random, 60));
securities.addAll(createRandomCapFloorCMSSpreads(random, 60));
if (securities.size() == 0) {
throw new OpenGammaRuntimeException("No valid securities were generated");
}
persistToPortfolio(securities, PORTFOLIO_NAME);
}
private Collection<FinancialSecurity> createRandomSwaps(final Random random, final int n) {
final Collection<FinancialSecurity> securities = new HashSet<>();
for (int i = 0; i < n; i++) {
FinancialSecurity security = null;
try {
final Tenor tenor = TENORS[random.nextInt(TENORS.length)];
security = makeSwap(random, tenor);
} catch (final Exception e) {
e.printStackTrace();
}
if (security != null) {
securities.add(security);
}
}
final StringBuilder sb = new StringBuilder();
sb.append("Parsed ").append(securities.size()).append(" swaps:\n");
for (final FinancialSecurity security : securities) {
sb.append("\t").append(security.getName()).append("\n");
}
s_logger.info(sb.toString());
return securities;
}
private Collection<FinancialSecurity> createRandomCapFloors(final Random random, final int n) {
final Collection<FinancialSecurity> securities = new HashSet<>();
for (int i = 0; i < n; i++) {
FinancialSecurity security = null;
try {
final Tenor tenor = TENORS[random.nextInt(TENORS.length)];
security = makeCapFloor(random, tenor);
} catch (final Exception e) {
e.printStackTrace();
}
if (security != null) {
securities.add(security);
}
}
final StringBuilder sb = new StringBuilder();
sb.append("Parsed ").append(securities.size()).append(" cap/floors:\n");
for (final FinancialSecurity security : securities) {
sb.append("\t").append(security.getName()).append("\n");
}
s_logger.info(sb.toString());
return securities;
}
private Collection<FinancialSecurity> createRandomCapFloorCMSSpreads(final Random random, final int n) {
final Collection<FinancialSecurity> securities = new HashSet<>();
for (int i = 0; i < n; i++) {
FinancialSecurity security = null;
try {
final Tenor tenor = TENORS[random.nextInt(TENORS.length)];
final Tenor payTenor = PAY_TENORS[random.nextInt(PAY_TENORS.length)];
final Tenor receiveTenor = RECEIVE_TENORS[random.nextInt(RECEIVE_TENORS.length)];
security = makeCMSCapFloorSpread(random, payTenor, receiveTenor, tenor, i);
} catch (final Exception e) {
e.printStackTrace();
}
if (security != null) {
securities.add(security);
}
}
final StringBuilder sb = new StringBuilder();
sb.append("Parsed ").append(securities.size()).append(" CMS cap/floor spreads:\n");
for (final FinancialSecurity security : securities) {
sb.append("\t").append(security.getName()).append("\n");
}
s_logger.info(sb.toString());
return securities;
}
private CapFloorSecurity makeCapFloor(final Random random, final Tenor tenor) {
final ZonedDateTime maturityDate = TRADE_DATE.plus(tenor.getPeriod()).atStartOfDay(ZoneOffset.UTC);
final boolean payer = random.nextBoolean();
final boolean cap = random.nextBoolean();
final double strike = getSwapRate(CURRENCY, TRADE_DATE, tenor) * (1 + ((0.5 - random.nextDouble()) / 30));
final double notional = 1000000 * (1 + random.nextInt(50));
final ExternalId underlyingId = TICKERS.get(tenor);
if (underlyingId == null) {
throw new OpenGammaRuntimeException("Could not get swap rate ticker for " + tenor);
}
final CapFloorSecurity security = new CapFloorSecurity(TRADE_DATE.atStartOfDay(ZoneOffset.UTC), maturityDate, notional, underlyingId, strike, PeriodFrequency.SEMI_ANNUAL,
Currency.USD, ACT_360, payer, cap, false);
security.setName("USD " + FORMAT.format(notional / 1000000) + (cap ? "MM cap " : "MM floor ") + "@ " + FORMAT.format(strike) +
(payer ? "%, pay " : "%, receive ") + tenor.getPeriod().normalized().getYears() + "Y ISDA fixing" +
" (" + TRADE_DATE.toString() + " - " + maturityDate.toLocalDate().toString() + ")");
security.addExternalId(ExternalId.of(ID_SCHEME, GUIDGenerator.generate().toString()));
return security;
}
private SwapSecurity makeSwap(final Random random, final Tenor tenor) {
final ZonedDateTime tradeDate = TRADE_DATE.atStartOfDay(ZoneOffset.UTC);
final ZonedDateTime maturityDate = tradeDate.plus(tenor.getPeriod());
final ExternalId iborReferenceRate = LIBOR_3M;
final PeriodFrequency frequency = PeriodFrequency.QUARTERLY;
final ExternalId cmsId = TICKERS.get(tenor);
if (cmsId == null) {
throw new OpenGammaRuntimeException("Could not get swap rate ticker for " + tenor);
}
final InterestRateNotional notional = new InterestRateNotional(Currency.USD, 1000000 * (1 + random.nextInt(50)));
final FloatingInterestRateLeg iborLeg = new FloatingInterestRateLeg(ACT_360, frequency, REGION, FOLLOWING, notional, true,
iborReferenceRate, FloatingRateType.IBOR);
final FloatingInterestRateLeg cmsLeg = new FloatingInterestRateLeg(ACT_360, frequency, REGION, FOLLOWING, notional, true,
cmsId, FloatingRateType.CMS);
SwapSecurity security;
boolean payIbor;
if (random.nextBoolean()) {
security = new SwapSecurity(tradeDate, tradeDate, maturityDate, COUNTERPARTY, iborLeg, cmsLeg);
payIbor = true;
} else {
security = new SwapSecurity(tradeDate, tradeDate, maturityDate, COUNTERPARTY, cmsLeg, iborLeg);
payIbor = false;
}
security.setName(CURRENCY.getCode() + " " + FORMAT.format(notional.getAmount() / 1000000) + "MM Swap, pay " +
(payIbor ? frequency.getPeriod().getMonths() + "M Libor, receive " + tenor.getPeriod().getYears() + "Y ISDA fixing (" :
tenor.getPeriod().getYears() + "Y ISDA fixing, receive " + frequency.getPeriod().getMonths() + "M Libor (") +
tradeDate.toLocalDate().toString() + " - " + maturityDate.toLocalDate().toString() + ")");
security.addExternalId(ExternalId.of(ID_SCHEME, GUIDGenerator.generate().toString()));
return security;
}
private CapFloorCMSSpreadSecurity makeCMSCapFloorSpread(final Random random, final Tenor payTenor, final Tenor receiveTenor, final Tenor maturity, final double strike) {
final ZonedDateTime tradeDate = TRADE_DATE.atStartOfDay(ZoneOffset.UTC);
final ZonedDateTime maturityDate = tradeDate.plus(maturity.getPeriod());
final boolean payer = random.nextBoolean();
final boolean cap = random.nextBoolean();
final ExternalId payIdentifier = TICKERS.get(payTenor);
if (payIdentifier == null) {
throw new OpenGammaRuntimeException("Could not get swap rate ticker for " + payTenor);
}
final ExternalId receiveIdentifier = TICKERS.get(receiveTenor);
if (receiveIdentifier == null) {
throw new OpenGammaRuntimeException("Could not get swap rate ticker for " + receiveTenor);
}
final double notional = 1000000 * (1 + random.nextInt(50));
final CapFloorCMSSpreadSecurity security = new CapFloorCMSSpreadSecurity(tradeDate, maturityDate, notional, payIdentifier, receiveIdentifier, strike,
PeriodFrequency.ANNUAL, CURRENCY, ACT_360, payer, cap);
security.setName(CURRENCY.getCode() + " " + FORMAT.format(notional / 1000000) + (cap ? "MM cap spread " : "MM floor spread ") + "@ " + FORMAT.format(strike) +
"%, pay " + payTenor.getPeriod().normalized().getYears() + "Y ISDA fixing" + ", receive " +
receiveTenor.getPeriod().normalized().getYears() + "Y ISDA fixing" +
" (" + tradeDate.toLocalDate().toString() + " - " + maturityDate.toLocalDate().toString() + ")");
security.addExternalId(ExternalId.of(ID_SCHEME, GUIDGenerator.generate().toString()));
return security;
}
private Double getSwapRate(final Currency ccy, final LocalDate tradeDate, final Tenor maturity) {
final HistoricalTimeSeriesSource historicalSource = getToolContext().getHistoricalTimeSeriesSource();
final MasterConfigSource configSource = new MasterConfigSource(getToolContext().getConfigMaster());
final ExternalId swapRateForMaturityIdentifier = getSwapRateFor(configSource, ccy, tradeDate, maturity);
if (swapRateForMaturityIdentifier == null) {
throw new OpenGammaRuntimeException("Couldn't get swap rate identifier for " + ccy + " [" + maturity + "]" + " from " + tradeDate);
}
final HistoricalTimeSeries fixedRateSeries = historicalSource.getHistoricalTimeSeries("PX_LAST",
swapRateForMaturityIdentifier.toBundle(), HistoricalTimeSeriesRatingFieldNames.DEFAULT_CONFIG_NAME, tradeDate.minusDays(30), true, tradeDate, true);
if (fixedRateSeries == null) {
throw new OpenGammaRuntimeException("Time series for " + swapRateForMaturityIdentifier + " was null");
}
if (fixedRateSeries.getTimeSeries().isEmpty()) {
throw new OpenGammaRuntimeException("Time series for " + swapRateForMaturityIdentifier + " was empty");
}
return fixedRateSeries.getTimeSeries().getLatestValue();
}
private static ExternalId getSwapRateFor(final ConfigSource configSource, final Currency ccy, final LocalDate tradeDate, final Tenor tenor) {
final CurveSpecificationBuilderConfiguration curveSpecConfig = configSource.getSingle(CurveSpecificationBuilderConfiguration.class, "DEFAULT_" + ccy.getCode(), VersionCorrection.LATEST);
if (curveSpecConfig == null) {
throw new OpenGammaRuntimeException("No curve spec builder configuration for DEFAULT_" + ccy.getCode());
}
ExternalId swapSecurity;
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);
}
return swapSecurity;
}
private void persistToPortfolio(final Collection<FinancialSecurity> securities, final String portfolioName) {
final PortfolioMaster portfolioMaster = getToolContext().getPortfolioMaster();
final PositionMaster positionMaster = getToolContext().getPositionMaster();
final SecurityMaster securityMaster = getToolContext().getSecurityMaster();
final ManageablePortfolioNode rootNode = new ManageablePortfolioNode(portfolioName);
final ManageablePortfolio portfolio = new ManageablePortfolio(portfolioName, rootNode);
final PortfolioDocument portfolioDoc = new PortfolioDocument();
portfolioDoc.setPortfolio(portfolio);
for (final FinancialSecurity security : securities) {
final SecurityDocument securityToAddDoc = new SecurityDocument();
securityToAddDoc.setSecurity(security);
securityMaster.add(securityToAddDoc);
final ManageablePosition position = new ManageablePosition(BigDecimal.ONE, security.getExternalIdBundle());
final PositionDocument addedDoc = positionMaster.add(new PositionDocument(position));
rootNode.addPosition(addedDoc.getUniqueId());
}
portfolioMaster.add(portfolioDoc);
}
}