PortfolioMonteCarloProduct.java
/*
* (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
*
* Created on 17.08.2013
*/
package net.finmath.montecarlo.products;
import java.util.Arrays;
import java.util.Optional;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import net.finmath.exception.CalculationException;
import net.finmath.montecarlo.AbstractMonteCarloProduct;
import net.finmath.montecarlo.MonteCarloProduct;
import net.finmath.montecarlo.MonteCarloSimulationModel;
import net.finmath.stochastic.RandomVariable;
/**
* A portfolio of products, each product being of AbstractMonteCarloProduct type.
* The valuation is performed multi-threaded over the portfolio of products.
*
* @author Christian Fries
* @version 1.0
*/
public class PortfolioMonteCarloProduct extends AbstractMonteCarloProduct {
private final MonteCarloProduct[] products;
private final double[] weights;
private final Optional<Integer> numberOfThreads;
/**
* Create a portfolio of products, each product being of AbstractMonteCarloProduct type
* and weighted with a given weight.
*
* @param products An array of products.
* @param weights An array of weights.
* @param numberOfThreads Number of parallel threads to used. Required to be > 0.
*/
public PortfolioMonteCarloProduct(
final MonteCarloProduct[] products,
final double[] weights,
final Optional<Integer> numberOfThreads) {
super();
this.products = products;
this.weights = weights;
this.numberOfThreads = numberOfThreads;
if(numberOfThreads.isPresent() && numberOfThreads.get() < 1) {
throw new IllegalArgumentException("The parameter numberOfThreads is required to be > 0 if present.");
}
}
/**
* Create a portfolio of products, each product being of AbstractMonteCarloProduct type
* and weighted with a given weight.
*
* @param products An array of products.
* @param weights An array of weights.
*/
public PortfolioMonteCarloProduct(
final MonteCarloProduct[] products,
final double[] weights) {
this(products, weights, Optional.empty());
}
/**
* Create a portfolio of products, each product being of AbstractMonteCarloProduct type.
*
* @param products An array of products.
*/
public PortfolioMonteCarloProduct(final MonteCarloProduct[] products) {
this(products, weightsOfOne(products.length));
}
/**
* Helper returns an array of 1.0's with given length.
*
* @param length Length of the array.
* @return Array of double with given length, each entry being 1.0.
*/
private static double[] weightsOfOne(final int length) {
final double[] weightsOfOne = new double[length];
Arrays.fill(weightsOfOne, 1.0);
return weightsOfOne;
}
@Override
public RandomVariable getValue(final double evaluationTime, final MonteCarloSimulationModel model) throws CalculationException {
if(products == null || products.length == 0) {
return null;
}
final int numberOfThreadsEffective = numberOfThreads.orElse(Runtime.getRuntime().availableProcessors());
final ExecutorService executor = Executors.newFixedThreadPool(numberOfThreadsEffective);
RandomVariable value = null;
try {
// Start calculation threads for each product
final Vector<Future<RandomVariable>> values = new Vector<>(products.length);
for(int i=0; i<products.length; i++) {
final MonteCarloProduct product = products[i];
final double weight = weights[i];
final Callable<RandomVariable> worker = new Callable<RandomVariable>() {
@Override
public RandomVariable call() throws CalculationException {
return product.getValue(evaluationTime, model).mult(weight);
}
};
values.add(i, executor.submit(worker));
}
// Collect and sum results
value = values.get(0).get();
for(int i=1; i<products.length; i++) {
value = value.add(values.get(i).get());
}
} catch (InterruptedException | ExecutionException e) {
throw new CalculationException(e.getCause());
} finally {
executor.shutdown();
}
return value;
}
}