/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.aggregation; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import com.opengamma.core.position.Portfolio; import com.opengamma.core.position.PortfolioNode; import com.opengamma.core.position.Position; import com.opengamma.core.position.PositionSource; import com.opengamma.core.position.impl.SimplePortfolio; import com.opengamma.core.position.impl.SimplePortfolioNode; import com.opengamma.core.security.SecuritySource; import com.opengamma.engine.view.compilation.PortfolioCompiler; import com.opengamma.financial.portfolio.save.SavePortfolio; import com.opengamma.id.UniqueId; import com.opengamma.id.UniqueIdSupplier; import com.opengamma.id.VersionCorrection; import com.opengamma.master.portfolio.ManageablePortfolio; import com.opengamma.master.portfolio.PortfolioMaster; import com.opengamma.master.portfolio.PortfolioSearchRequest; import com.opengamma.master.portfolio.PortfolioSearchResult; import com.opengamma.master.position.PositionMaster; /** * An aggregator of portfolios. */ public final class PortfolioAggregator { private static final Logger s_logger = LoggerFactory.getLogger(PortfolioAggregator.class); private static final UniqueIdSupplier s_syntheticIdentifiers = new UniqueIdSupplier("PortfolioAggregator"); private final List<AggregationFunction<?>> _aggregationFunctions; public PortfolioAggregator(AggregationFunction<?>... aggregationFunctions) { _aggregationFunctions = Arrays.asList(aggregationFunctions); } public PortfolioAggregator(Collection<AggregationFunction<?>> aggregationFunctions) { _aggregationFunctions = new ArrayList<AggregationFunction<?>>(aggregationFunctions); } private static UniqueId createSyntheticIdentifier() { return s_syntheticIdentifiers.get(); } public Portfolio aggregate(Portfolio inputPortfolio) { UniqueId portfolioId = inputPortfolio.getUniqueId(); UniqueId aggId; if (portfolioId != null) { String aggPortfolioId = buildPortfolioName(portfolioId.getValue()); aggId = UniqueId.of(portfolioId.getScheme(), aggPortfolioId); } else { aggId = createSyntheticIdentifier(); } String aggPortfolioName = buildPortfolioName(inputPortfolio.getName()); List<Position> flattenedPortfolio = flatten(inputPortfolio); final SimplePortfolioNode root = new SimplePortfolioNode(createSyntheticIdentifier(), buildPortfolioName("Portfolio")); SimplePortfolio aggPortfolio = new SimplePortfolio(aggId, aggPortfolioName, root); aggregate(root, flattenedPortfolio, new ArrayDeque<AggregationFunction<?>>(_aggregationFunctions)); aggPortfolio.setAttributes(inputPortfolio.getAttributes()); return aggPortfolio; } public static List<Position> flatten(Portfolio inputPortfolio) { List<Position> positions = Lists.newArrayList(); flatten(inputPortfolio.getRootNode(), positions); return positions; } private static void flatten(PortfolioNode portfolioNode, List<Position> flattenedPortfolio) { flattenedPortfolio.addAll(portfolioNode.getPositions()); for (PortfolioNode subNode : portfolioNode.getChildNodes()) { flatten(subNode, flattenedPortfolio); } } private void aggregate(SimplePortfolioNode inputNode, List<Position> flattenedPortfolio, Queue<AggregationFunction<?>> functionList) { AggregationFunction<?> nextFunction = functionList.remove(); s_logger.debug("Aggregating {} positions by {}", flattenedPortfolio, nextFunction); @SuppressWarnings("unchecked") Map<String, List<Position>> buckets = new TreeMap<String, List<Position>>((Comparator<? super String>) nextFunction); for (Object entry : nextFunction.getRequiredEntries()) { buckets.put(entry.toString(), new ArrayList<Position>()); } // drop into buckets - could drop straight into tree but this is easier because we can use faster lookups as we're going. for (Position position : flattenedPortfolio) { Object obj = nextFunction.classifyPosition(position); if (obj != null) { String name = obj.toString(); if (buckets.containsKey(name)) { buckets.get(name).add(position); } else { ArrayList<Position> list = new ArrayList<Position>(); list.add(position); buckets.put(name, list); } } } for (String bucketName : buckets.keySet()) { SimplePortfolioNode newNode = new SimplePortfolioNode(); newNode.setUniqueId(createSyntheticIdentifier()); newNode.setParentNodeId(inputNode.getUniqueId()); newNode.setName(bucketName); inputNode.addChildNode(newNode); List<Position> bucket = buckets.get(bucketName); Collections.sort(bucket, nextFunction.getPositionComparator()); if (functionList.isEmpty() || bucket.isEmpty()) { //IGN-138 - don't build huge empty portfolios for (Position position : bucket) { newNode.addPosition(position); } } else { aggregate(newNode, bucket, new ArrayDeque<AggregationFunction<?>>(functionList)); // make a copy for each bucket. } } } private String buildPortfolioName(String existingName) { StringBuilder aggregatedPortfolioName = new StringBuilder(); aggregatedPortfolioName.append(existingName); aggregatedPortfolioName.append(" aggregated by "); for (AggregationFunction<?> aggFunction : _aggregationFunctions) { aggregatedPortfolioName.append(aggFunction.getName()); aggregatedPortfolioName.append(", "); } if (_aggregationFunctions.size() > 0) { aggregatedPortfolioName.delete(aggregatedPortfolioName.length() - 2, aggregatedPortfolioName.length()); // remember it's end index _exclusive_ } return aggregatedPortfolioName.toString(); } public static void aggregate(String portfolioName, String aggregationName, PortfolioMaster portfolioMaster, PositionMaster positionMaster, PositionSource positionSource, SecuritySource secSource, AggregationFunction<?>[] aggregationFunctions, boolean split) { PortfolioSearchRequest searchReq = new PortfolioSearchRequest(); searchReq.setName(portfolioName); s_logger.info("Searching for portfolio " + portfolioName + "..."); PortfolioSearchResult searchResult = portfolioMaster.search(searchReq); s_logger.info("Done. Got " + searchResult.getDocuments().size() + " results"); ManageablePortfolio manageablePortfolio = searchResult.getFirstPortfolio(); if (manageablePortfolio == null) { s_logger.error("Portfolio " + portfolioName + " was not found"); System.exit(1); } s_logger.info("Reloading portfolio from position source..."); Portfolio portfolio = positionSource.getPortfolio(manageablePortfolio.getUniqueId(), VersionCorrection.LATEST); if (portfolio == null) { s_logger.error("Portfolio " + portfolioName + " was not found from PositionSource"); System.exit(1); } s_logger.info("Done."); ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(20); s_logger.info("Resolving portfolio positions and securities..."); Portfolio resolvedPortfolio = PortfolioCompiler.resolvePortfolio(portfolio, newFixedThreadPool, secSource); if (resolvedPortfolio == null) { s_logger.error("Portfolio " + portfolioName + " was not correctly resolved by PortfolioCompiler"); System.exit(1); } s_logger.info("Resolution Complete."); PortfolioAggregator aggregator = new PortfolioAggregator(aggregationFunctions); s_logger.info("Beginning aggregation"); Portfolio aggregatedPortfolio = aggregator.aggregate(resolvedPortfolio); s_logger.info("Aggregation complete, about to persist..."); if (aggregatedPortfolio == null) { s_logger.error("Portfolio " + portfolioName + " was not correctly aggregated by the Portfolio Aggregator"); System.exit(1); } SavePortfolio savePortfolio = new SavePortfolio(newFixedThreadPool, portfolioMaster, positionMaster); if (split) { for (PortfolioNode portfolioNode : aggregatedPortfolio.getRootNode().getChildNodes()) { String splitPortfolioName = portfolioName + " (" + aggregationName + " " + portfolioNode.getName() + ")"; SimplePortfolioNode root = new SimplePortfolioNode("root"); root.addChildNode(portfolioNode); Portfolio splitPortfolio = new SimplePortfolio(splitPortfolioName, root); splitPortfolio.setAttributes(aggregatedPortfolio.getAttributes()); s_logger.info("Saving split portfolio " + portfolioName + "..."); savePortfolio.savePortfolio(splitPortfolio, true); } } else { savePortfolio.savePortfolio(aggregatedPortfolio, true); // update matching named portfolio. } s_logger.info("Saved."); // Shut down thread pool before returning newFixedThreadPool.shutdown(); } }