Portfolio.java

/*
 * (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
 *
 * Created on 02.03.2014
 */

package net.finmath.marketdata2.products;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import net.finmath.marketdata2.model.AnalyticModel;
import net.finmath.stochastic.RandomVariable;

/**
 * Implements the valuation of a portfolio of products implementing
 * <code>AnalyticProductInterface</code>.
 *
 * @author Christian Fries
 * @version 1.0
 */
public class Portfolio extends AbstractAnalyticProduct implements AnalyticProduct {

	private final ArrayList<AnalyticProduct>	products;
	private final ArrayList<Double>					weights;

	/**
	 * Create a portfolio of products implementing
	 * <code>AnalyticProductInterface</code>. The portfolio consists
	 * of an array of products and a corresponding array of weights.
	 * The value of the portfolio is given by the sum over
	 * <code>
	 * 	weights[i] * products.get(i).getValue(evaluationTime, model)
	 * </code>
	 *
	 * Note that a product in the array of products may itself be
	 * a <code>Portfolio</code> (hence you may easily combine portfolios).
	 *
	 * @param products Array of products implementing <code>AnalyticProductInterface</code>.
	 * @param weights Array of weights used in the valuation as a multiplicator.
	 */
	public Portfolio(final List<AnalyticProduct> products, final List<Double> weights) {
		super();
		this.products = new ArrayList<>();
		this.weights = new ArrayList<>();
		this.products.addAll(products);
		this.weights.addAll(weights);
	}

	/**
	 * Create a portfolio of products implementing
	 * <code>AnalyticProductInterface</code>. The portfolio consists
	 * of an array of products and a corresponding array of weights.
	 * The value of the portfolio is given by the sum over
	 * <code>
	 * 	weights[i] * products.get(i).getValue(evaluationTime, model)
	 * </code>
	 *
	 * The portfolio is created by taking all products and weights of a given portfolio
	 * and adding other given products and weights.
	 *
	 * @param portfolio A given portfolio, which will become part of this portfolio.
	 * @param products Array of products implementing <code>AnalyticProductInterface</code>.
	 * @param weights Array of weights used in the valuation as a multiplicator.
	 */
	public Portfolio(final Portfolio portfolio, final List<AnalyticProduct> products, final List<Double> weights) {
		super();
		this.products = new ArrayList<>();
		this.weights = new ArrayList<>();
		this.products.addAll(portfolio.getProducts());
		this.weights.addAll(portfolio.getWeights());
		this.products.addAll(products);
		this.weights.addAll(weights);
	}

	/**
	 * Create a portfolio consisting of a single product with a given weight.
	 *
	 * @param product A product, implementing  implementing <code>AnalyticProductInterface</code>.
	 * @param weight A weight used in the valuation as a multiplicator.
	 */
	public Portfolio(final AnalyticProduct product, final double weight) {
		super();
		products = new ArrayList<>();
		weights = new ArrayList<>();
		products.add(product);
		weights.add(weight);
	}

	/**
	 * Create a portfolio of products implementing
	 * <code>AnalyticProductInterface</code>.
	 *
	 * The value of the portfolio is given by the sum over
	 * <code>
	 * 	products.get(i).getValue(evaluationTime, model)
	 * </code>
	 *
	 * Note that a product in the array of products may itself be
	 * a <code>Portfolio</code> (hence you may easily combine portfolios).
	 *
	 * @param products Array of products implementing <code>AnalyticProductInterface</code>.
	 */
	public Portfolio(final List<AnalyticProduct> products) {
		this(products, Collections.nCopies(products.size(), Double.valueOf(1.0)));
	}

	@Override
	public RandomVariable getValue(final double evaluationTime, final AnalyticModel model) {
		RandomVariable value = model.getRandomVariableForConstant(0.0);

		final List<RandomVariable> productValues	= products.parallelStream().map(new Function<AnalyticProduct, RandomVariable>() {
			@Override
			public RandomVariable apply(final AnalyticProduct product) {
				return product.getValue(evaluationTime, model);
			}
		}).collect(Collectors.toList());
		final List<RandomVariable> weightsRandomVariables = weights.parallelStream().map(new Function<Double, RandomVariable>() {
			@Override
			public RandomVariable apply(final Double weight) {
				return model.getRandomVariableForConstant(weight);
			}
		}).collect(Collectors.toList());

		value = value.addSumProduct(productValues, weightsRandomVariables);

		return value;
	}

	/**
	 * Returns the list of products as an unmodifiable list.
	 * Calling <code>add</code> on this list will result in an {@link UnsupportedOperationException}.
	 *
	 * @return The list of products as an unmodifiable list.
	 */
	public List<AnalyticProduct> getProducts() {
		return Collections.unmodifiableList(products);
	}

	/**
	 * Returns the list of weights as an unmodifiable list.
	 * Calling <code>add</code> on this list will result in an {@link UnsupportedOperationException}.
	 *
	 * @return The list of weights as an unmodifiable list.
	 */
	public List<Double> getWeights() {
		return Collections.unmodifiableList(weights);
	}
}