Java tutorial
/** * Copyright (C) 2009 - 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.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.fudgemsg.FudgeMsg; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; import org.threeten.bp.LocalDate; import org.threeten.bp.Period; import com.google.common.collect.BiMap; import com.google.common.collect.Sets; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.bbg.BloombergConstants; import com.opengamma.bbg.BloombergFields; import com.opengamma.bbg.referencedata.ReferenceDataProvider; import com.opengamma.bbg.util.BloombergDataUtils; import com.opengamma.bbg.util.BloombergTickerParserEQOption; import com.opengamma.component.tool.AbstractTool; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries; import com.opengamma.core.id.ExternalSchemes; import com.opengamma.financial.security.equity.EquitySecurity; import com.opengamma.financial.security.equity.GICSCode; import com.opengamma.financial.security.option.OptionType; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.id.UniqueId; import com.opengamma.integration.tool.IntegrationToolContext; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesInfoDocument; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesInfoSearchRequest; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesInfoSearchResult; 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.portfolio.PortfolioSearchRequest; import com.opengamma.master.portfolio.PortfolioSearchResult; import com.opengamma.master.position.ManageablePosition; import com.opengamma.master.position.ManageableTrade; import com.opengamma.master.position.PositionDocument; import com.opengamma.master.security.ManageableSecurity; import com.opengamma.master.security.SecuritySearchRequest; import com.opengamma.master.security.SecuritySearchResult; import com.opengamma.scripts.Scriptable; import com.opengamma.util.tuple.Pair; /** * A portfolio loader which generates a sensible portfolio of liquid equities and options on them Also see DemoEquityOptionPortfolioLoader. */ @Scriptable public class DemoEquityOptionCollarPortfolioLoader extends AbstractTool<IntegrationToolContext> { private static final String TOOL_NAME = "Demo Equity Option Portfolio Loader"; private static final String PORTFOLIO_NAME_OPT = "p"; private static final String OPTION_DEPTH_OPT = "d"; private static final String NUM_CONTRACTS_OPT = "n"; private static final String NUM_INDEX_MEMBERS_OPT = "m"; private static final String BLOOMBERG_EQUITY_TICKER_SUFFIX = " Equity"; private static final Logger s_logger = LoggerFactory.getLogger(DemoEquityOptionCollarPortfolioLoader.class); private static final Map<String, String> INDEXES_TO_EXCHANGE = getIndexToExchangeMap(); private static final Set<String> EXCLUDED_SECTORS = Sets.newHashSet("Financials"); private static final Period[] MEMBER_OPTION_PERIODS = new Period[] { Period.ofMonths(3), Period.ofMonths(6) }; private BigDecimal _numContracts; private int _numOptions; private int _numMembers; /** * In units of currency */ private static final BigDecimal VALUE_OF_UNDERLYING = BigDecimal.valueOf(100000); /** * The default genearted portfolio name. */ public static final String PORTFOLIO_NAME = "Equity Option Portfolio"; private static Map<String, String> getIndexToExchangeMap() { final Map<String, String> ret = new HashMap<String, String>(); ret.put("SPX", "US"); //S&P 500 -> combined US // ret.put("IBX", "BZ"); //Sao Paulo Stock Exchange IBrX Index -> combined Brazil //ret.put("TW50", "TT"); // FTSE TWSE Taiwan 50 Indx -> Taiwan Stock Exchange return ret; } //------------------------------------------------------------------------- /** * Main method to run the tool. No arguments are needed. * * @param args the arguments, unused */ public static void main(final String[] args) { // CSIGNORE final boolean success = new DemoEquityOptionCollarPortfolioLoader().initAndRun(args, IntegrationToolContext.class); System.exit(success ? 0 : 1); } //------------------------------------------------------------------------- protected ManageablePortfolio generatePortfolio(final String portfolioName) { final ReferenceDataProvider referenceDataProvider = getToolContext().getBloombergReferenceDataProvider(); final ManageablePortfolio portfolio = new ManageablePortfolio(portfolioName); //Is this a hack? final ManageablePortfolioNode rootNode = portfolio.getRootNode(); portfolio.setRootNode(rootNode); // String indexTickerSuffix = " Index"; final Set<String> memberEquities = new HashSet<String>(); for (final Entry<String, String> entry : INDEXES_TO_EXCHANGE.entrySet()) { final String indexTickerSuffix = " Index"; final String underlying = entry.getKey(); final String ticker = underlying + indexTickerSuffix; //don't add index (delete at some point) // addNodes(rootNode, ticker, false, INDEX_OPTION_PERIODS); final Set<String> indexMembers = BloombergDataUtils.getIndexMembers(referenceDataProvider, ticker); for (final String member : indexMembers) { final String symbol = getBloombergEquitySymbol(entry.getValue(), member); //time series errors for Walmart //Todo: investegate & fix if ("WMT US Equity".equals(symbol)) { continue; } memberEquities.add(symbol); } } // Sort the symbols for the current index by market cap (highest to lowest), skipping any in the list of EXCLUDED_SECTORS final TreeMap<Double, String> equityByMarketCap = new TreeMap<Double, String>(); final Map<String, FudgeMsg> refDataMap = referenceDataProvider.getReferenceData(memberEquities, Sets .newHashSet(BloombergFields.CURRENT_MARKET_CAP_FIELD, BloombergConstants.FIELD_GICS_SUB_INDUSTRY)); for (final String equity : memberEquities) { final FudgeMsg fieldData = refDataMap.get(equity); if (fieldData == null) { throw new OpenGammaRuntimeException("Information not found for equity: " + equity); } final String gicsCodeString = fieldData.getString(BloombergConstants.FIELD_GICS_SUB_INDUSTRY); if (gicsCodeString == null) { continue; } final GICSCode gicsCode = GICSCode.of(gicsCodeString); if (EXCLUDED_SECTORS.contains(gicsCode.getSectorDescription())) { continue; } final Double marketCap = fieldData.getDouble(BloombergFields.CURRENT_MARKET_CAP_FIELD); if (marketCap != null) { equityByMarketCap.put(marketCap, equity); } } // Add a given number of symbols (MEMBERS_DEPTH) to the portfolio and store in a List // When adding to the portfolio, add a collar of options with PVs distributed equally +/- around 0 int count = 0; final List<String> chosenEquities = new ArrayList<String>(); for (final Entry<Double, String> entry : equityByMarketCap.descendingMap().entrySet()) { try { addNodes(rootNode, entry.getValue(), true, MEMBER_OPTION_PERIODS); chosenEquities.add(entry.getValue()); if (++count >= _numMembers) { break; } } catch (final RuntimeException e) { s_logger.warn("Caught exception", e); } } s_logger.info("Generated collar portfolio for {}", chosenEquities); return portfolio; } private String getBloombergEquitySymbol(final String base, final String member) { return member.split(" ")[0] + " " + base + BLOOMBERG_EQUITY_TICKER_SUFFIX; } private void addNodes(final ManageablePortfolioNode rootNode, final String underlying, final boolean includeUnderlying, final Period[] expiries) { final ExternalId ticker = ExternalSchemes.bloombergTickerSecurityId(underlying); ManageableSecurity underlyingSecurity = null; if (includeUnderlying) { underlyingSecurity = getOrLoadEquity(ticker); } final ExternalIdBundle bundle = underlyingSecurity == null ? ExternalIdBundle.of(ticker) : underlyingSecurity.getExternalIdBundle(); final HistoricalTimeSeriesInfoDocument timeSeriesInfo = getOrLoadTimeSeries(ticker, bundle); final double estimatedCurrentStrike = getOrLoadMostRecentPoint(timeSeriesInfo); final Set<ExternalId> optionChain = getOptionChain(ticker); //TODO: reuse positions/nodes? final String longName = underlyingSecurity == null ? "" : underlyingSecurity.getName(); final String formattedName = MessageFormatter.format("[{}] {}", underlying, longName).getMessage(); final ManageablePortfolioNode equityNode = new ManageablePortfolioNode(formattedName); final BigDecimal underlyingAmount = VALUE_OF_UNDERLYING.divide(BigDecimal.valueOf(estimatedCurrentStrike), BigDecimal.ROUND_HALF_EVEN); if (includeUnderlying) { addPosition(equityNode, underlyingAmount, ticker); } final TreeMap<LocalDate, Set<BloombergTickerParserEQOption>> optionsByExpiry = new TreeMap<LocalDate, Set<BloombergTickerParserEQOption>>(); for (final ExternalId optionTicker : optionChain) { s_logger.debug("Got option {}", optionTicker); final BloombergTickerParserEQOption optionInfo = BloombergTickerParserEQOption .getOptionParser(optionTicker); s_logger.debug("Got option info {}", optionInfo); final LocalDate key = optionInfo.getExpiry(); Set<BloombergTickerParserEQOption> set = optionsByExpiry.get(key); if (set == null) { set = new HashSet<BloombergTickerParserEQOption>(); optionsByExpiry.put(key, set); } set.add(optionInfo); } final Set<ExternalId> tickersToLoad = new HashSet<ExternalId>(); final BigDecimal expiryCount = BigDecimal.valueOf(expiries.length); final BigDecimal defaultAmountAtExpiry = underlyingAmount.divide(expiryCount, BigDecimal.ROUND_DOWN); final BigDecimal spareAmountAtExpiry = defaultAmountAtExpiry.add(BigDecimal.ONE); int spareCount = underlyingAmount.subtract(defaultAmountAtExpiry.multiply(expiryCount)).intValue(); for (final Period bucketPeriod : expiries) { final ManageablePortfolioNode bucketNode = new ManageablePortfolioNode( bucketPeriod.toString().substring(1)); final LocalDate nowish = LocalDate.now().withDayOfMonth(20); //This avoids us picking different options every time this script is run final LocalDate targetExpiry = nowish.plus(bucketPeriod); final LocalDate chosenExpiry = optionsByExpiry.floorKey(targetExpiry); if (chosenExpiry == null) { s_logger.info("No options for {} on {}", targetExpiry, underlying); continue; } s_logger.info("Using time {} for bucket {} ({})", new Object[] { chosenExpiry, bucketPeriod, targetExpiry }); final Set<BloombergTickerParserEQOption> optionsAtExpiry = optionsByExpiry.get(chosenExpiry); final TreeMap<Double, Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>> optionsByStrike = new TreeMap<>(); for (final BloombergTickerParserEQOption option : optionsAtExpiry) { // s_logger.info("option {}", option); final double key = option.getStrike(); Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption> pair = optionsByStrike.get(key); if (pair == null) { pair = Pair.of(null, null); } if (option.getOptionType() == OptionType.CALL) { pair = Pair.of(option, pair.getSecond()); } else { pair = Pair.of(pair.getFirst(), option); } optionsByStrike.put(key, pair); } //cascading collar? final BigDecimal amountAtExpiry = spareCount-- > 0 ? spareAmountAtExpiry : defaultAmountAtExpiry; s_logger.info(" est strike {}", estimatedCurrentStrike); final Double[] strikes = optionsByStrike.keySet().toArray(new Double[0]); int strikeIndex = Arrays.binarySearch(strikes, estimatedCurrentStrike); if (strikeIndex < 0) { strikeIndex = -(1 + strikeIndex); } s_logger.info("strikes length {} index {} strike of index {}", new Object[] { Integer.valueOf(strikes.length), Integer.valueOf(strikeIndex), Double.valueOf(strikes[strikeIndex]) }); int minIndex = strikeIndex - _numOptions; minIndex = Math.max(0, minIndex); int maxIndex = strikeIndex + _numOptions; maxIndex = Math.min(strikes.length - 1, maxIndex); s_logger.info("min {} max {}", Integer.valueOf(minIndex), Integer.valueOf(maxIndex)); final StringBuffer sb = new StringBuffer("strikes: ["); for (int j = minIndex; j <= maxIndex; j++) { sb.append(" "); sb.append(strikes[j]); } sb.append(" ]"); s_logger.info(sb.toString()); //Short Calls final ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>> calls = new ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>>(); for (int j = minIndex; j < strikeIndex; j++) { final Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption> pair = optionsByStrike .get(strikes[j]); if (pair == null) { throw new OpenGammaRuntimeException("no pair for strike" + strikes[j]); } calls.add(pair); } spreadOptions(bucketNode, calls, OptionType.CALL, -1, tickersToLoad, amountAtExpiry, includeUnderlying, calls.size()); // Long Puts final ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>> puts = new ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>>(); for (int j = strikeIndex + 1; j <= maxIndex; j++) { final Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption> pair = optionsByStrike .get(strikes[j]); if (pair == null) { throw new OpenGammaRuntimeException("no pair for strike" + strikes[j]); } puts.add(pair); } spreadOptions(bucketNode, puts, OptionType.PUT, 1, tickersToLoad, amountAtExpiry, includeUnderlying, puts.size()); if (bucketNode.getChildNodes().size() + bucketNode.getPositionIds().size() > 0) { equityNode.addChildNode(bucketNode); //Avoid generating empty nodes } } for (final ExternalId optionTicker : tickersToLoad) { final ManageableSecurity loaded = getOrLoadSecurity(optionTicker); if (loaded == null) { throw new OpenGammaRuntimeException("Unexpected option type " + loaded); } //TODO [LAPANA-29] Should be able to do this for index options too if (includeUnderlying) { try { final HistoricalTimeSeriesInfoDocument loadedTs = getOrLoadTimeSeries(optionTicker, loaded.getExternalIdBundle()); if (loadedTs == null) { throw new OpenGammaRuntimeException("Failed to get time series for " + loaded); } } catch (final Exception ex) { s_logger.info("Failed to get time series for " + loaded, ex); } } } if (equityNode.getPositionIds().size() + equityNode.getChildNodes().size() > 0) { rootNode.addChildNode(equityNode); } } private void spreadOptions(final ManageablePortfolioNode bucketNode, final Collection<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>> options, final OptionType type, final int scale, final Set<ExternalId> tickersToLoad, final BigDecimal underlyingAmount, final boolean includeUnderlying, final int targetNumber) { if (targetNumber == 0) { return; } final Collection<BloombergTickerParserEQOption> chosen = new ArrayList<BloombergTickerParserEQOption>(); int remaining = targetNumber; for (final Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption> pair : options) { BloombergTickerParserEQOption option; if (type == OptionType.PUT) { option = pair.getSecond(); } else { option = pair.getFirst(); } //TODO [LAPANA-29] Should be able to do this for index options too if (includeUnderlying) { try { final HistoricalTimeSeriesInfoDocument loadedTs = getOrLoadTimeSeries(option.getIdentifier()); final HistoricalTimeSeries ts = getToolContext().getHistoricalTimeSeriesSource() .getHistoricalTimeSeries(loadedTs.getUniqueId(), LocalDate.now().minusWeeks(1), true, LocalDate.now(), true); if (ts.getTimeSeries().isEmpty()) { s_logger.info("No recent time series points for " + option.getIdentifier()); // leave in for now // continue; //This option is not liquid enough for us } } catch (final Exception ex) { s_logger.info("Failed to get time series for " + option.getIdentifier(), ex); //TODO: stop refetching this series each time continue; //This option is not liquid enough for us } } chosen.add(option); remaining--; if (remaining == 0) { break; } } if (chosen.size() == 0) { s_logger.warn("Couldn't find any liquid " + type + " options from " + options); return; //TODO: should we try another expiry? } for (final BloombergTickerParserEQOption option : chosen) { tickersToLoad.add(option.getIdentifier()); addPosition(bucketNode, BigDecimal.valueOf(scale).multiply(_numContracts), option.getIdentifier()); } } private void addPosition(final ManageablePortfolioNode node, final BigDecimal amount, final ExternalId optionTicker) { final ManageablePosition position = new ManageablePosition(amount, optionTicker); final LocalDate tradeDate = getRandomTradeDate(optionTicker); final ManageableTrade trade = new ManageableTrade(amount, optionTicker, tradeDate, null, ExternalId.of("CPARTY", "BACS")); position.addTrade(trade); final PositionDocument doc = new PositionDocument(position); final PositionDocument added = getToolContext().getPositionMaster().add(doc); node.addPosition(added); } private LocalDate getRandomTradeDate(final ExternalId ticker) { final int tradeAge = (int) (3 + (Math.random() * 30)); final LocalDate tradeDate = LocalDate.now().minusDays(tradeAge); //TODO: pick a date for which PX_LAST is known return tradeDate; } private Set<ExternalId> getOptionChain(final ExternalId ticker) { if (ticker.getScheme() != ExternalSchemes.BLOOMBERG_TICKER) { throw new OpenGammaRuntimeException("Not a bloomberg ticker " + ticker); } final ReferenceDataProvider referenceDataProvider = getToolContext().getBloombergReferenceDataProvider(); final Set<ExternalId> optionChain = BloombergDataUtils.getOptionChain(referenceDataProvider, ticker.getValue()); //TODO [BBG-88] this query shouldn't get cached permanently if (optionChain == null) { throw new OpenGammaRuntimeException("Failed to get option chain for " + ticker); } s_logger.info("Got option chain {}", optionChain); return optionChain; } private double getOrLoadMostRecentPoint(HistoricalTimeSeriesInfoDocument timeSeriesInfo) { HistoricalTimeSeries timeSeries = getAllowedRecentPoints(timeSeriesInfo); if (timeSeries == null || timeSeries.getTimeSeries().isEmpty()) { if (timeSeries == null) { timeSeriesInfo = loadTimeSeries(timeSeriesInfo); } else if (timeSeries.getTimeSeries().isEmpty()) { timeSeries = updateTimeSeries(timeSeries); } timeSeries = getAllowedRecentPoints(timeSeriesInfo); if (timeSeries == null || timeSeries.getTimeSeries().isEmpty()) { throw new OpenGammaRuntimeException("Couldn't load recent points for " + timeSeriesInfo); } } final Double latestValue = timeSeries.getTimeSeries().getLatestValue(); if (latestValue == null) { throw new OpenGammaRuntimeException("Unexpected null latest vaule"); } return latestValue; } private HistoricalTimeSeries getAllowedRecentPoints(final HistoricalTimeSeriesInfoDocument timeSeriesInfo) { final LocalDate from = oldestTimeSeriesAllowed(); final HistoricalTimeSeries timeSeries = getToolContext().getHistoricalTimeSeriesSource() .getHistoricalTimeSeries(timeSeriesInfo.getUniqueId().toLatest(), from, true, LocalDate.now(), true); return timeSeries; } private HistoricalTimeSeriesInfoDocument getOrLoadTimeSeries(final ExternalId ticker) { return getOrLoadTimeSeries(ticker, ExternalIdBundle.of(ticker)); } private HistoricalTimeSeriesInfoDocument getOrLoadTimeSeries(final ExternalId ticker, final ExternalIdBundle idBundle) { final ExternalIdBundle searchBundle = idBundle.withoutScheme(ExternalSchemes.ISIN); //For things which move country, e.g. ISIN(VALE5 BZ Equity) == ISIN(RIODF US Equity) final HistoricalTimeSeriesInfoSearchRequest htsRequest = new HistoricalTimeSeriesInfoSearchRequest( searchBundle); htsRequest.setDataField("PX_LAST"); final HistoricalTimeSeriesInfoSearchResult htsSearch = getToolContext().getHistoricalTimeSeriesMaster() .search(htsRequest); switch (htsSearch.getDocuments().size()) { case 0: return loadTimeSeries(idBundle); case 1: break; default: throw new OpenGammaRuntimeException("Multiple time series match " + htsSearch); } final HistoricalTimeSeriesInfoDocument timeSeriesInfo = htsSearch.getDocuments().get(0); s_logger.debug("Loaded time series info {} for underlying {}", timeSeriesInfo, ticker); return timeSeriesInfo; } private HistoricalTimeSeriesInfoDocument loadTimeSeries(ExternalIdBundle idBundle) { final ReferenceDataProvider referenceDataProvider = getToolContext().getBloombergReferenceDataProvider(); if (idBundle.getExternalId(ExternalSchemes.BLOOMBERG_BUID) == null && idBundle.getExternalId(ExternalSchemes.BLOOMBERG_TICKER) != null) { //For some reason loading some series by TICKER fails, but BUID works final BiMap<String, ExternalIdBundle> map = BloombergDataUtils .convertToBloombergBuidKeys(Collections.singleton(idBundle), referenceDataProvider); if (map.size() != 1) { throw new OpenGammaRuntimeException("Failed to get buid"); } for (final String key : map.keySet()) { final String buid = referenceDataProvider.getReferenceDataValue(key, BloombergConstants.FIELD_ID_BBG_UNIQUE); idBundle = idBundle.withExternalId(ExternalSchemes.bloombergBuidSecurityId(buid)); } } final ExternalIdBundle searchBundle = idBundle.withoutScheme(ExternalSchemes.ISIN); // For things which move country, e.g. ISIN(VALE5 BZ Equity) == ISIN(RIODF US Equity) final Map<ExternalId, UniqueId> timeSeries = getToolContext().getHistoricalTimeSeriesLoader() .loadTimeSeries(searchBundle.getExternalIds(), "UNKNOWN", "PX_LAST", LocalDate.now().minusYears(1), null); if (timeSeries.size() != 1) { throw new OpenGammaRuntimeException("Failed to load time series " + idBundle + " " + timeSeries); } for (final UniqueId uid : timeSeries.values()) { return getToolContext().getHistoricalTimeSeriesMaster().get(uid); } throw new OpenGammaRuntimeException("Unexpected state"); } private HistoricalTimeSeries updateTimeSeries(final HistoricalTimeSeries timeSeries) { if (!getToolContext().getHistoricalTimeSeriesLoader().updateTimeSeries(timeSeries.getUniqueId())) { throw new OpenGammaRuntimeException("Failed to update time series " + timeSeries); } //Force a cache miss on the source final HistoricalTimeSeriesInfoDocument newUid = getToolContext().getHistoricalTimeSeriesMaster() .get(timeSeries.getUniqueId().toLatest()); return getToolContext().getHistoricalTimeSeriesSource().getHistoricalTimeSeries(newUid.getUniqueId()); } private HistoricalTimeSeriesInfoDocument loadTimeSeries(final HistoricalTimeSeriesInfoDocument timeSeriesInfo) { final ExternalIdBundle idBundle = timeSeriesInfo.getInfo().getExternalIdBundle().toBundle(LocalDate.now()); return loadTimeSeries(idBundle); } private LocalDate oldestTimeSeriesAllowed() { return LocalDate.now().minusWeeks(1); } private ManageableSecurity getOrLoadEquity(final ExternalId ticker) { final ManageableSecurity underlyingSecurity = getOrLoadSecurity(ticker); if (!EquitySecurity.SECURITY_TYPE.equals(underlyingSecurity.getSecurityType())) { throw new OpenGammaRuntimeException("Underlying is not an equity"); } return underlyingSecurity; } private ManageableSecurity getOrLoadSecurity(final ExternalId ticker) { final SecuritySearchResult underlyingSearch = getToolContext().getSecurityMaster() .search(new SecuritySearchRequest(ticker)); switch (underlyingSearch.getDocuments().size()) { case 0: s_logger.debug("Loading security for underlying {}", ticker); return loadSecurity(ticker); case 1: return underlyingSearch.getSingleSecurity(); default: // Duplicate securities in the master s_logger.info("Multiple securities matched search for ticker {}. Using the first. {}", ticker, underlyingSearch); return underlyingSearch.getFirstSecurity(); } } private ManageableSecurity loadSecurity(final ExternalId ticker) { final ExternalIdBundle tickerBundle = ExternalIdBundle.of(ticker); final Collection<ExternalIdBundle> bundles = Collections.singleton(tickerBundle); final Map<ExternalIdBundle, UniqueId> loaded = getToolContext().getSecurityLoader().loadSecurities(bundles); final UniqueId loadedSec = loaded.get(tickerBundle); if (loadedSec == null) { throw new OpenGammaRuntimeException("Failed to load security for " + ticker); } return getToolContext().getSecurityMaster().get(loadedSec).getSecurity(); } /** * Stores the portfolio. * * @param portfolio the portfolio, not null */ private void storePortfolio(final ManageablePortfolio portfolio) { final PortfolioMaster portfolioMaster = getToolContext().getPortfolioMaster(); final PortfolioSearchRequest req = new PortfolioSearchRequest(); req.setName(portfolio.getName()); final PortfolioSearchResult result = portfolioMaster.search(req); switch (result.getDocuments().size()) { case 0: s_logger.info("Creating new portfolio"); portfolioMaster.add(new PortfolioDocument(portfolio)); break; case 1: final UniqueId previousId = result.getDocuments().get(0).getUniqueId(); s_logger.info("Updating portfolio {}", previousId); portfolio.setUniqueId(previousId); final PortfolioDocument document = new PortfolioDocument(portfolio); document.setUniqueId(previousId); portfolioMaster.update(document); break; default: throw new OpenGammaRuntimeException("Multiple portfolios matching " + req); } } @Override protected Options createOptions(final boolean mandatoryConfigResource) { final Options options = super.createOptions(mandatoryConfigResource); options.addOption(createPortfolioNameOption()); options.addOption(createOptionDepthOption()); options.addOption(createNumContractsOption()); options.addOption(createNumMembersOption()); return options; } private static Option createPortfolioNameOption() { OptionBuilder.withLongOpt("portfolio"); OptionBuilder.withDescription("The name of the portfolio to create/update"); OptionBuilder.hasArg(); OptionBuilder.withArgName("resource"); OptionBuilder.isRequired(); return OptionBuilder.create(PORTFOLIO_NAME_OPT); } private static Option createOptionDepthOption() { OptionBuilder.withLongOpt("depth"); OptionBuilder.withDescription("Number of options on either side of the strike price"); OptionBuilder.hasArg(); OptionBuilder.withArgName("resource"); OptionBuilder.isRequired(); return OptionBuilder.create(OPTION_DEPTH_OPT); } private static Option createNumContractsOption() { OptionBuilder.withLongOpt("contracts"); OptionBuilder.withDescription("Number of contracts for each option"); OptionBuilder.hasArg(); OptionBuilder.withArgName("resource"); OptionBuilder.isRequired(); return OptionBuilder.create(NUM_CONTRACTS_OPT); } private static Option createNumMembersOption() { OptionBuilder.withLongOpt("members"); OptionBuilder.withDescription("Number underlyers from index to include"); OptionBuilder.hasArg(); OptionBuilder.withArgName("resource"); OptionBuilder.isRequired(); return OptionBuilder.create(NUM_INDEX_MEMBERS_OPT); } @Override protected void doRun() throws Exception { s_logger.info(TOOL_NAME + " is initialising..."); s_logger.info("Current working directory is " + System.getProperty("user.dir")); s_logger.info("Using portfolio \"{}\"", PORTFOLIO_NAME); s_logger.info("num index members: " + _numMembers); s_logger.info("Option Depth: " + _numOptions); s_logger.info("num contracts: {}", _numContracts.toString()); final ManageablePortfolio portfolio = generatePortfolio(PORTFOLIO_NAME); storePortfolio(portfolio); s_logger.info(TOOL_NAME + " is finished."); } public void setNumContracts(final BigDecimal numContracts) { _numContracts = numContracts; } public void setNumOptions(final int numOptions) { _numOptions = numOptions; } public void setNumMembers(final int numMembers) { _numMembers = numMembers; } }