/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.option.pricing.fourier; import org.apache.commons.lang.Validate; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackFunctionData; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.BlackPriceFunction; import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.integration.Integrator1D; import com.opengamma.analytics.math.integration.RungeKuttaIntegrator1D; import com.opengamma.analytics.math.number.ComplexNumber; /** * */ public class FourierPricer { private static final IntegralLimitCalculator LIMIT_CALCULATOR = new IntegralLimitCalculator(); private static final BlackPriceFunction BLACK_PRICE_FUNCTION = new BlackPriceFunction(); private final Integrator1D<Double, Double> _integrator; public FourierPricer() { this(new RungeKuttaIntegrator1D()); } public FourierPricer(final Integrator1D<Double, Double> integrator) { Validate.notNull(integrator, "null integrator"); _integrator = integrator; } public double price(final BlackFunctionData data, final EuropeanVanillaOption option, final MartingaleCharacteristicExponent ce, final double alpha, final double limitTolerance) { return price(data, option, ce, alpha, limitTolerance, false); } public double price(final BlackFunctionData data, final EuropeanVanillaOption option, final MartingaleCharacteristicExponent ce, final double alpha, final double limitTolerance, final boolean useVarianceReduction) { Validate.notNull(data, "data"); Validate.notNull(option, "option"); Validate.notNull(ce, "characteristic exponent"); Validate.isTrue(limitTolerance > 0, "limit tolerance must be > 0"); Validate.isTrue(alpha <= ce.getLargestAlpha() && alpha >= ce.getSmallestAlpha(), "The value of alpha is not valid for the Characteristic Exponent and will most likely lead to mispricing. Choose a value between " + ce.getSmallestAlpha() + " and " + ce.getLargestAlpha()); final EuropeanPriceIntegrand integrand = new EuropeanPriceIntegrand(ce, alpha, useVarianceReduction); final EuropeanCallFourierTransform psi = new EuropeanCallFourierTransform(ce); final double strike = option.getStrike(); final double t = option.getTimeToExpiry(); final boolean isCall = option.isCall(); final double forward = data.getForward(); final double discountFactor = data.getDiscountFactor(); final Function1D<ComplexNumber, ComplexNumber> characteristicFunction = psi.getFunction(t); final double xMax = LIMIT_CALCULATOR.solve(characteristicFunction, alpha, limitTolerance); final Function1D<Double, Double> func = integrand.getFunction(data, option); final double integral = Math.exp(-alpha * Math.log(strike / forward)) * _integrator.integrate(func, 0.0, xMax) / Math.PI; if (useVarianceReduction) { final double black = BLACK_PRICE_FUNCTION.getPriceFunction(option).evaluate(data); final double diff = discountFactor * forward * integral; return diff + black; } if (isCall) { if (alpha > 0.0) { return discountFactor * forward * integral; } else if (alpha < -1.0) { return discountFactor * (forward * (1 + integral) - strike); } else { return discountFactor * forward * (integral + 1); } } if (alpha > 0.0) { return discountFactor * (forward * (integral - 1) + strike); } else if (alpha < -1.0) { return discountFactor * forward * integral; } return discountFactor * (forward * integral + strike); } public double priceFromVol(final BlackFunctionData data, final EuropeanVanillaOption option, final MartingaleCharacteristicExponent ce, final double alpha, final double limitTolerance, final boolean useVarianceReduction) { final double forward = data.getForward(); final double discountFactor = data.getDiscountFactor(); final double t = option.getTimeToExpiry(); final double strike = option.getStrike(); final EuropeanPriceIntegrand integrand = new EuropeanPriceIntegrand(ce, alpha, useVarianceReduction); final EuropeanCallFourierTransform callFourierTransform = new EuropeanCallFourierTransform(ce); final Function1D<ComplexNumber, ComplexNumber> psi = callFourierTransform.getFunction(t); final double xMax = LIMIT_CALCULATOR.solve(psi, alpha, limitTolerance); final double integral = Math.exp(-alpha * Math.log(strike / forward)) * _integrator.integrate(integrand.getFunction(data, option), 0.0, xMax) / Math.PI; final double black = BLACK_PRICE_FUNCTION.getPriceFunction(option).evaluate(data); final double diff = discountFactor * forward * integral; return diff + black; } }