Portfolio.java

/*
 * (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
 *
 * Created on 15.02.2004
 */
package net.finmath.montecarlo.interestrate.products;

import java.util.Set;

import net.finmath.exception.CalculationException;
import net.finmath.montecarlo.RandomVariableFromDoubleArray;
import net.finmath.montecarlo.interestrate.TermStructureMonteCarloSimulationModel;
import net.finmath.montecarlo.interestrate.products.components.AbstractProductComponent;
import net.finmath.stochastic.RandomVariable;

/**
 * Implements the pricing of a portfolio of AbstractLIBORMonteCarloProduct products
 * under a AbstractLIBORMarketModel. The products can be scaled by weights.
 *
 * The value of the portfolio is that of
 * \( \sum_{i=0}^{n} weights\[i\] \cdot products\[i\] \text{.} \)
 *
 * Note: Currently the products have to be of the same currency.
 *
 * @author Christian Fries
 * @date 08.09.2006
 * @version 1.2
 */
public class Portfolio extends AbstractProductComponent {

	private static final long serialVersionUID = -1360506093081238482L;

	private final AbstractTermStructureMonteCarloProduct[]	products;
	private final double[]							weights;

	/**
	 * Creates a portfolio consisting of a single product and a weight.
	 *
	 * The currency of this portfolio is the currency of the product.
	 *
	 * @param product A product.
	 * @param weight A weight.
	 */
	public Portfolio(final AbstractTermStructureMonteCarloProduct product, final double weight) {
		super(product.getCurrency());
		products = new AbstractTermStructureMonteCarloProduct[] { product };
		weights = new double[] { weight };
	}

	/**
	 * Creates a portfolio consisting of a set of products and a weights.
	 *
	 * Note: Currently the products have to be of the same currency.
	 *
	 * @param products An array of products.
	 * @param weights An array of weights (having the same lengths as the array of products).
	 */
	public Portfolio(final AbstractTermStructureMonteCarloProduct[] products, final double[] weights) {
		super();
		final String currency = products[0].getCurrency();
		for(final AbstractTermStructureMonteCarloProduct product : products) {
			if(currency != null && !currency.equals(product.getCurrency())) {
				throw new IllegalArgumentException("Product currencies do not match. Please use a constructor providing the currency of the result.");
			}
		}

		this.products = products;
		this.weights = weights;
	}

	/**
	 * Creates a portfolio consisting of a set of products and a weights.
	 *
	 * Note: Currently the products have to be of the same currency, namely the one provided.
	 *
	 * @param currency The currency of the value of this portfolio when calling <code>getValue</code>.
	 * @param products An array of products.
	 * @param weights An array of weights (having the same lengths as the array of products).
	 */
	public Portfolio(final String currency, final AbstractTermStructureMonteCarloProduct[] products, final double[] weights) {
		super(currency);

		for(final AbstractTermStructureMonteCarloProduct product : products) {
			if(!currency.equals(product.getCurrency())) {
				throw new IllegalArgumentException("Product currencies do not match. Currency conversion (via model FX) is not supported yet.");
			}
		}

		this.products = products;
		this.weights = weights;
	}

	@Override
	public String getCurrency() {
		// @TODO We report only the currency of the first item, because mixed currency portfolios are currently not allowed.
		return (products != null && products.length > 0) ? products[0].getCurrency() : null;
	}

	@Override
	public Set<String> queryUnderlyings() {
		Set<String> underlyingNames = null;
		for(final TermStructureMonteCarloProduct product : products) {
			Set<String> productUnderlyingNames;
			if(product instanceof AbstractProductComponent) {
				productUnderlyingNames = ((AbstractProductComponent)product).queryUnderlyings();
			} else {
				throw new IllegalArgumentException("Underlying cannot be queried for underlyings.");
			}

			if(productUnderlyingNames != null) {
				if(underlyingNames == null) {
					underlyingNames = productUnderlyingNames;
				} else {
					underlyingNames.addAll(productUnderlyingNames);
				}
			}
		}
		return underlyingNames;
	}

	/**
	 * This method returns the value random variable of the product within the specified model, evaluated at a given evalutationTime.
	 * Note: For a lattice this is often the value conditional to evalutationTime, for a Monte-Carlo simulation this is the (sum of) value discounted to evaluation time.
	 * Cashflows prior evaluationTime are not considered.
	 *
	 * @TODO The conversion between different currencies is currently not performed.
	 * @param evaluationTime The time on which this products value should be observed.
	 * @param model The model used to price the product.
	 * @return The random variable representing the value of the product discounted to evaluation time
	 * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
	 */
	@Override
	public RandomVariable getValue(final double evaluationTime, final TermStructureMonteCarloSimulationModel model) throws CalculationException {
		RandomVariable values = new RandomVariableFromDoubleArray(0.0);

		for(int productIndex = 0; productIndex < products.length; productIndex++) {
			final RandomVariable    valueOfProduct = products[productIndex].getValue(evaluationTime, model);
			final double   				   weightOfProduct = weights[productIndex];

			values = values.addProduct(valueOfProduct, weightOfProduct);
		}
		return values;
	}

	/**
	 * @return the products
	 */
	public TermStructureMonteCarloProduct[] getProducts() {
		return products.clone();
	}

	/**
	 * @return the weights
	 */
	public double[] getWeights() {
		return weights.clone();
	}
}