/* * (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christianfries.com. * * Created on 05.04.2015 */ package net.finmath.montecarlo.hybridassetinterestrate; import net.finmath.exception.CalculationException; import net.finmath.functions.AnalyticFormulas; import net.finmath.marketdata.model.curves.DiscountCurveInterface; import net.finmath.montecarlo.BrownianMotionInterface; import net.finmath.montecarlo.assetderivativevaluation.AssetModelMonteCarloSimulationInterface; import net.finmath.montecarlo.assetderivativevaluation.MonteCarloMultiAssetBlackScholesModel; import net.finmath.montecarlo.assetderivativevaluation.products.EuropeanOption; import net.finmath.montecarlo.interestrate.LIBORModelMonteCarloSimulationInterface; import net.finmath.optimizer.LevenbergMarquardt; import net.finmath.optimizer.OptimizerInterface; import net.finmath.optimizer.SolverException; /** * Helper factory to create a simple equity hybrid LIBOR market model. * * @author Christian Fries */ public class ModelFactory { private static ModelFactory modelFactory; /** * Private constructor. */ private ModelFactory() { // TODO Auto-generated constructor stub } public synchronized static ModelFactory getInstance() { if(modelFactory == null) modelFactory = new ModelFactory(); return modelFactory; } /** * Create a simple equity hybrid LIBOR market model with a calibration of the equity processes * to a given Black-Scholes implied volatility. * * @param baseModel LIBOR model providing the stochastic numeraire. * @param brownianMotion {@link BrownianMotionInterface} for the asset process. * @param initialValues Initial value of the asset process. * @param riskFreeRate Not used (internally used to generate paths, will be later adjusted) * @param correlations Correlation of the asset processes. * @param maturities Maturities of the options (one for each asset process). * @param strikes Strikes of the options (one for each asset process). * @param volatilities Implied volatilities of the options (one for each asset process). * @param discountCurve Discount curve used for the final hybrid model (not used in calibration). * @return An object implementing {@link HybridAssetLIBORModelMonteCarloSimulationInterface}, where each asset process is calibrated to a given option. * @throws CalculationException Thrown if calibration fails. */ public HybridAssetLIBORModelMonteCarloSimulationInterface getHybridAssetLIBORModel(final LIBORModelMonteCarloSimulationInterface baseModel, final BrownianMotionInterface brownianMotion, final double[] initialValues, final double riskFreeRate, final double[][] correlations, final double[] maturities, final double[] strikes, final double[] volatilities, final DiscountCurveInterface discountCurve) throws CalculationException { OptimizerInterface optimizer = new LevenbergMarquardt(volatilities /*initialParameters*/, volatilities /*targetValues*/, 100 /*maxIteration*/, 1 /*numberOfThreads*/) { private static final long serialVersionUID = -9199565564991442848L; @Override public void setValues(double[] parameters, double[] values) throws SolverException { AssetModelMonteCarloSimulationInterface model = new MonteCarloMultiAssetBlackScholesModel(brownianMotion, initialValues, riskFreeRate, parameters, correlations); HybridAssetLIBORModelMonteCarloSimulation hybridModel = new HybridAssetLIBORModelMonteCarloSimulation(baseModel, model); try { for(int assetIndex=0; assetIndex<values.length; assetIndex++) { double df = hybridModel.getNumeraire(maturities[assetIndex]).invert().getAverage(); double spot = hybridModel.getAssetValue(0.0, assetIndex).getAverage(); EuropeanOption option = new EuropeanOption(maturities[assetIndex], strikes[assetIndex], assetIndex); double valueOptoin = option.getValue(hybridModel); double impliedVol = AnalyticFormulas.blackScholesOptionImpliedVolatility(spot/df, maturities[assetIndex]/*optionMaturity*/, strikes[assetIndex]/*optionStrike*/, df /*payoffUnit*/, valueOptoin); values[assetIndex] = impliedVol; } } catch (CalculationException e) { throw new SolverException(e); } } }; try { optimizer.run(); } catch (SolverException e) { if(e.getCause() instanceof CalculationException) throw (CalculationException)e.getCause(); else throw new CalculationException(e); } AssetModelMonteCarloSimulationInterface model = new MonteCarloMultiAssetBlackScholesModel(brownianMotion, initialValues, riskFreeRate, optimizer.getBestFitParameters(), correlations); /* * Test calibration */ HybridAssetLIBORModelMonteCarloSimulation hybridModelWithoutDiscountAdjustment = new HybridAssetLIBORModelMonteCarloSimulation(baseModel, model, null); for(int assetIndex=0; assetIndex<volatilities.length; assetIndex++) { double df = hybridModelWithoutDiscountAdjustment.getNumeraire(maturities[assetIndex]).invert().getAverage(); double spot = hybridModelWithoutDiscountAdjustment.getAssetValue(0.0, assetIndex).getAverage(); EuropeanOption option = new EuropeanOption(maturities[assetIndex], strikes[assetIndex], assetIndex); double valueOptoin = option.getValue(hybridModelWithoutDiscountAdjustment); double impliedVol = AnalyticFormulas.blackScholesOptionImpliedVolatility(spot/df, maturities[assetIndex]/*optionMaturity*/, strikes[assetIndex]/*optionStrike*/, df /*payoffUnit*/, valueOptoin); if(Math.abs(impliedVol - volatilities[assetIndex]) > 0.01) throw new CalculationException("Calibration failed"); } /* * Construct model with discounting (options will then use the discounting spread adjustment). */ HybridAssetLIBORModelMonteCarloSimulation hybridModel = new HybridAssetLIBORModelMonteCarloSimulation(baseModel, model, discountCurve); return hybridModel; } }