/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.payments.provider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.financial.interestrate.PresentValueSABRSensitivityDataBundle;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CapFloorCMS;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CapFloorCMSSpread;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponCMS;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.NormalFunctionData;
import com.opengamma.analytics.financial.model.option.pricing.analytic.formula.NormalPriceFunction;
import com.opengamma.analytics.financial.model.volatility.NormalImpliedVolatilityFormula;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParRateCurveSensitivityDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParRateDiscountingCalculator;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderInterface;
import com.opengamma.analytics.financial.provider.description.interestrate.SABRSwaptionProviderInterface;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MulticurveSensitivity;
import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity;
import com.opengamma.analytics.math.function.DoubleFunction1D;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.function.RealPolynomialFunction1D;
import com.opengamma.analytics.math.rootfinding.BrentSingleRootFinder;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;
import com.opengamma.util.tuple.DoublesPair;
/**
* Class used to compute the price of a CMS spread cap/floor with the bi-normal approach with correlation by strike.
* OpenGamma implementation note: Bi-normal with correlation by strike approach to CMS spread pricing, version 1.1, June 2011.
*/
public class CapFloorCMSSpreadSABRBinormalMethod {
/**
* The par rate calculator.
*/
private static final ParRateDiscountingCalculator PRDC = ParRateDiscountingCalculator.getInstance();
/**
* The par rate sensitivity calculator.
*/
private static final ParRateCurveSensitivityDiscountingCalculator PRCSDC = ParRateCurveSensitivityDiscountingCalculator.getInstance();
/**
* The formula used to compute the implied volatility.
*/
private static final NormalImpliedVolatilityFormula NORMAL_IMPLIED_VOLATILITY = new NormalImpliedVolatilityFormula();
/**
* The formula used to compute the implied volatility.
*/
private static final NormalPriceFunction NORMAL_PRICE = new NormalPriceFunction();
/**
* The method to compute the price of CMS cap/floors.
*/
private final CapFloorCMSSABRReplicationAbstractMethod _methodCmsCap;
/**
* The method to compute the price o CMS coupons.
*/
private final CouponCMSSABRReplicationGenericMethod _methodCmsCoupon;
/**
* The correlation as function of the strike.
*/
private final DoubleFunction1D _correlation;
/**
* Constructor of the CMS spread cap/floor method with the CMS cap and coupon methods.
* @param correlation The rates correlation.
* @param methodCmsCap The pricing method for the CMS cap/floor.
* @param methodCmsCoupon The pricing method for the CMS coupons.
*/
public CapFloorCMSSpreadSABRBinormalMethod(final DoubleFunction1D correlation, final CapFloorCMSSABRReplicationAbstractMethod methodCmsCap,
final CouponCMSSABRReplicationGenericMethod methodCmsCoupon) {
Validate.notNull(correlation, "Correlation");
_correlation = correlation;
_methodCmsCap = methodCmsCap;
_methodCmsCoupon = methodCmsCoupon;
}
/**
* Gets the correlation (rho) as function of the strike.
* @return The correlation
*/
public DoubleFunction1D getCorrelation() {
return _correlation;
}
/**
* Compute the present value of a CMS spread cap/floor in the binormal approach.
* @param cmsSpread The CMS spread cap/floor.
* @param sabrData The SABR data bundle.
* @return The present value.
*/
public MultipleCurrencyAmount presentValue(final CapFloorCMSSpread cmsSpread, final SABRSwaptionProviderInterface sabrData) {
ArgumentChecker.notNull(cmsSpread, "CMA cap/floor");
ArgumentChecker.notNull(sabrData, "SABR swaption provider");
final Currency ccy = cmsSpread.getCurrency();
final MulticurveProviderInterface multicurves = sabrData.getMulticurveProvider();
final double forward1 = cmsSpread.getUnderlyingSwap1().accept(PRDC, multicurves);
final double forward2 = cmsSpread.getUnderlyingSwap2().accept(PRDC, multicurves);
CouponCMS cmsCoupon1 = CouponCMS.from(cmsSpread, cmsSpread.getUnderlyingSwap1(), cmsSpread.getSettlementTime());
cmsCoupon1 = cmsCoupon1.withNotional(Math.abs(cmsCoupon1.getNotional()));
CouponCMS cmsCoupon2 = CouponCMS.from(cmsSpread, cmsSpread.getUnderlyingSwap2(), cmsSpread.getSettlementTime());
cmsCoupon2 = cmsCoupon2.withNotional(Math.abs(cmsCoupon2.getNotional()));
final CapFloorCMS cmsCap1 = CapFloorCMS.from(cmsCoupon1, forward1, true);
final CapFloorCMS cmsCap2 = CapFloorCMS.from(cmsCoupon2, forward2, true);
final double cmsCoupon1Price = _methodCmsCoupon.presentValue(cmsCoupon1, sabrData).getAmount(ccy);
final double cmsCoupon2Price = _methodCmsCoupon.presentValue(cmsCoupon2, sabrData).getAmount(ccy);
final double cmsCap1Price = _methodCmsCap.presentValue(cmsCap1, sabrData).getAmount(ccy);
final double cmsCap2Price = _methodCmsCap.presentValue(cmsCap2, sabrData).getAmount(ccy);
final double discountFactorPayment = multicurves.getDiscountFactor(ccy, cmsSpread.getPaymentTime());
final NormalFunctionData dataCap1 = new NormalFunctionData(cmsCoupon1Price / (discountFactorPayment * cmsCap1.getNotional() * cmsCap1.getPaymentYearFraction()), discountFactorPayment
* cmsCap1.getNotional() * cmsCap1.getPaymentYearFraction(), 0.0);
final EuropeanVanillaOption optionCap1 = new EuropeanVanillaOption(forward1, cmsSpread.getFixingTime(), true);
double cmsCap1ImpliedVolatility = 0;
try {
cmsCap1ImpliedVolatility = NORMAL_IMPLIED_VOLATILITY.getImpliedVolatility(dataCap1, optionCap1, cmsCap1Price);
} catch (final Exception e) {
//TODO
}
final NormalFunctionData dataCap2 = new NormalFunctionData(cmsCoupon2Price / (discountFactorPayment * cmsCap2.getNotional() * cmsCap2.getPaymentYearFraction()), discountFactorPayment
* cmsCap2.getNotional() * cmsCap2.getPaymentYearFraction(), cmsCap1ImpliedVolatility);
final EuropeanVanillaOption optionCap2 = new EuropeanVanillaOption(forward2, cmsSpread.getFixingTime(), true);
double cmsCap2ImpliedVolatility = 0;
try {
cmsCap2ImpliedVolatility = NORMAL_IMPLIED_VOLATILITY.getImpliedVolatility(dataCap2, optionCap2, cmsCap2Price);
} catch (final Exception e) {
//TODO
}
final double cmsSpreadImpliedVolatility = Math.sqrt(cmsCap1ImpliedVolatility * cmsCap1ImpliedVolatility - 2 * _correlation.evaluate(cmsSpread.getStrike()) * cmsCap1ImpliedVolatility
* cmsCap2ImpliedVolatility + cmsCap2ImpliedVolatility * cmsCap2ImpliedVolatility);
final NormalFunctionData dataSpread = new NormalFunctionData(
(cmsCoupon1Price - cmsCoupon2Price) / (discountFactorPayment * Math.abs(cmsSpread.getNotional()) * cmsSpread.getPaymentYearFraction()), discountFactorPayment * cmsSpread.getNotional()
* cmsSpread.getPaymentYearFraction(), cmsSpreadImpliedVolatility);
final EuropeanVanillaOption optionSpread = new EuropeanVanillaOption(cmsSpread.getStrike(), cmsSpread.getFixingTime(), cmsSpread.isCap());
final Function1D<NormalFunctionData, Double> normalFunction = NORMAL_PRICE.getPriceFunction(optionSpread);
final double cmsSpreadPrice = normalFunction.evaluate(dataSpread);
return MultipleCurrencyAmount.of(cmsSpread.getCurrency(), cmsSpreadPrice);
}
/**
* Compute the implied correlation for a specific CMS spread cap/floor from the given price. The model correlation structure is not used.
* @param cmsSpread The CMS spread cap/floor.
* @param sabrData The SABR data bundle.
* @param price The CMS spread price.
* @return The implied correlation.
*/
public double impliedCorrelation(final CapFloorCMSSpread cmsSpread, final SABRSwaptionProviderInterface sabrData, final double price) {
final SolveCorrelation function = new SolveCorrelation(cmsSpread, sabrData, price);
final BrentSingleRootFinder finder = new BrentSingleRootFinder();
final double correlation = finder.getRoot(function, -0.999, 0.999);
return correlation;
}
/**
* Computes the present value curves sensitivity of a CMS spread cap/floor in the bi-normal approach.
* For the CMS cap/floor volatility calibration, ATM forward strikes are used.
* @param cmsSpread The CMS spread cap/floor.
* @param sabrData The SABR data bundle.
* @return The present value curve sensitivity.
*/
public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final CapFloorCMSSpread cmsSpread, final SABRSwaptionProviderInterface sabrData) {
ArgumentChecker.notNull(cmsSpread, "CMA cap/floor");
ArgumentChecker.notNull(sabrData, "SABR swaption provider");
final Currency ccy = cmsSpread.getCurrency();
final MulticurveProviderInterface multicurves = sabrData.getMulticurveProvider();
// Forward sweep
final double strike1 = cmsSpread.getUnderlyingSwap1().accept(PRDC, multicurves);
final double strike2 = cmsSpread.getUnderlyingSwap2().accept(PRDC, multicurves);
CouponCMS cmsCoupon1 = CouponCMS.from(cmsSpread, cmsSpread.getUnderlyingSwap1(), cmsSpread.getSettlementTime());
cmsCoupon1 = cmsCoupon1.withNotional(Math.abs(cmsCoupon1.getNotional()));
CouponCMS cmsCoupon2 = CouponCMS.from(cmsSpread, cmsSpread.getUnderlyingSwap2(), cmsSpread.getSettlementTime());
cmsCoupon2 = cmsCoupon2.withNotional(Math.abs(cmsCoupon2.getNotional()));
final CapFloorCMS cmsCap1 = CapFloorCMS.from(cmsCoupon1, strike1, true); // ATM forward cap CMS
final CapFloorCMS cmsCap2 = CapFloorCMS.from(cmsCoupon2, strike2, true); // ATM forward cap CMS
final double cmsCoupon1Pv = _methodCmsCoupon.presentValue(cmsCoupon1, sabrData).getAmount(ccy);
final double cmsCoupon2Pv = _methodCmsCoupon.presentValue(cmsCoupon2, sabrData).getAmount(ccy);
final double cmsCap1Pv = _methodCmsCap.presentValue(cmsCap1, sabrData).getAmount(ccy);
final double cmsCap2Pv = _methodCmsCap.presentValue(cmsCap2, sabrData).getAmount(ccy);
final double discountFactorPayment = multicurves.getDiscountFactor(ccy, cmsSpread.getPaymentTime());
final double factor = discountFactorPayment * cmsCap1.getNotional() * cmsCap1.getPaymentYearFraction();
final double expectation1 = cmsCoupon1Pv / factor;
final double expectation2 = cmsCoupon2Pv / factor;
NormalFunctionData dataCap1 = new NormalFunctionData(expectation1, factor, 0.0);
final EuropeanVanillaOption optionCap1 = new EuropeanVanillaOption(strike1, cmsSpread.getFixingTime(), true);
double cmsCap1ImpliedVolatility = 0;
try {
cmsCap1ImpliedVolatility = NORMAL_IMPLIED_VOLATILITY.getImpliedVolatility(dataCap1, optionCap1, cmsCap1Pv);
} catch (final Exception e) {
//TODO
}
NormalFunctionData dataCap2 = new NormalFunctionData(expectation2, factor, cmsCap1ImpliedVolatility);
final EuropeanVanillaOption optionCap2 = new EuropeanVanillaOption(strike2, cmsSpread.getFixingTime(), true);
double cmsCap2ImpliedVolatility = 0;
try {
cmsCap2ImpliedVolatility = NORMAL_IMPLIED_VOLATILITY.getImpliedVolatility(dataCap2, optionCap2, cmsCap2Pv);
} catch (final Exception e) {
//TODO
}
final double rho = _correlation.evaluate(cmsSpread.getStrike());
final double cmsSpreadImpliedVolatility = Math.sqrt(cmsCap1ImpliedVolatility * cmsCap1ImpliedVolatility - 2 * rho * cmsCap1ImpliedVolatility * cmsCap2ImpliedVolatility + cmsCap2ImpliedVolatility
* cmsCap2ImpliedVolatility);
final NormalFunctionData dataSpread = new NormalFunctionData(expectation1 - expectation2, discountFactorPayment * cmsSpread.getNotional() * cmsSpread.getPaymentYearFraction(),
cmsSpreadImpliedVolatility);
final EuropeanVanillaOption optionSpread = new EuropeanVanillaOption(cmsSpread.getStrike(), cmsSpread.getFixingTime(), cmsSpread.isCap());
final double[] cmsSpreadPvDerivative = new double[3];
final double cmsSpreadPv = NORMAL_PRICE.getPriceAdjoint(optionSpread, dataSpread, cmsSpreadPvDerivative);
// Backward sweep
final double cmsSpreadPvBar = 1.0;
final double cmsSpreadImpliedVolatilityBar = cmsSpreadPvDerivative[1] * cmsSpreadPvBar;
final double cmsCap2ImpliedVolatilityBar = (cmsCap2ImpliedVolatility - rho * cmsCap1ImpliedVolatility) / cmsSpreadImpliedVolatility * cmsSpreadImpliedVolatilityBar; // OK
final double cmsCap1ImpliedVolatilityBar = (cmsCap1ImpliedVolatility - rho * cmsCap2ImpliedVolatility) / cmsSpreadImpliedVolatility * cmsSpreadImpliedVolatilityBar; // OK
dataCap2 = new NormalFunctionData(expectation2, factor, cmsCap2ImpliedVolatility);
final double[] cmsCap2PriceNormalDerivative = new double[3];
NORMAL_PRICE.getPriceAdjoint(optionCap2, dataCap2, cmsCap2PriceNormalDerivative);
final double expectation2Bar = -cmsSpreadPvDerivative[0] * cmsSpreadPvBar + -cmsCap2PriceNormalDerivative[0] / cmsCap2PriceNormalDerivative[1] * cmsCap2ImpliedVolatilityBar; // OK
dataCap1 = new NormalFunctionData(expectation1, factor, cmsCap1ImpliedVolatility);
final double[] cmsCap1PriceNormalDerivative = new double[3];
NORMAL_PRICE.getPriceAdjoint(optionCap1, dataCap1, cmsCap1PriceNormalDerivative);
final double expectation1Bar = cmsSpreadPvDerivative[0] * cmsSpreadPvBar + -cmsCap1PriceNormalDerivative[0] / cmsCap1PriceNormalDerivative[1] * cmsCap1ImpliedVolatilityBar; // OK
final double factorBar = -cmsCoupon1Pv / (factor * factor) * expectation1Bar + -cmsCoupon2Pv / (factor * factor) * expectation2Bar - cmsCap2Pv / factor / cmsCap2PriceNormalDerivative[1]
* cmsCap2ImpliedVolatilityBar - cmsCap1Pv / factor / cmsCap1PriceNormalDerivative[1] * cmsCap1ImpliedVolatilityBar; // OK
final double discountFactorPaymentBar = cmsCap1.getNotional() * cmsCap1.getPaymentYearFraction() * factorBar + cmsSpreadPv / discountFactorPayment * cmsSpreadPvBar;
final double cmsCap2PvBar = 1.0 / cmsCap2PriceNormalDerivative[1] * cmsCap2ImpliedVolatilityBar; // OK
final double cmsCap1PvBar = 1.0 / cmsCap1PriceNormalDerivative[1] * cmsCap1ImpliedVolatilityBar; // OK
final double cmsCoupon2PvBar = expectation2Bar / factor; // OK
final double cmsCoupon1PvBar = expectation1Bar / factor; // OK
//Calibration strike dependency -- START
double strike1Bar = -cmsCap1PriceNormalDerivative[2] / cmsCap1PriceNormalDerivative[1] * cmsCap1ImpliedVolatilityBar;
double strike2Bar = -cmsCap2PriceNormalDerivative[2] / cmsCap2PriceNormalDerivative[1] * cmsCap2ImpliedVolatilityBar;
strike1Bar += _methodCmsCap.presentValueStrikeSensitivity(cmsCap1, sabrData) * cmsCap1PvBar;
strike2Bar += _methodCmsCap.presentValueStrikeSensitivity(cmsCap2, sabrData) * cmsCap2PvBar;
final MulticurveSensitivity forward1CurveSensitivity = cmsSpread.getUnderlyingSwap1().accept(PRCSDC, multicurves);
final MulticurveSensitivity forward2CurveSensitivity = cmsSpread.getUnderlyingSwap2().accept(PRCSDC, multicurves);
//Calibration strike dependency -- END
final MultipleCurrencyMulticurveSensitivity cmsCoupon1CurveSensitivity = _methodCmsCoupon.presentValueCurveSensitivity(cmsCoupon1, sabrData);
final MultipleCurrencyMulticurveSensitivity cmsCoupon2CurveSensitivity = _methodCmsCoupon.presentValueCurveSensitivity(cmsCoupon2, sabrData);
final MultipleCurrencyMulticurveSensitivity cmsCap1CurveSensitivity = _methodCmsCap.presentValueCurveSensitivity(cmsCap1, sabrData);
final MultipleCurrencyMulticurveSensitivity cmsCap2CurveSensitivity = _methodCmsCap.presentValueCurveSensitivity(cmsCap2, sabrData);
final List<DoublesPair> list = new ArrayList<>();
list.add(DoublesPair.of(cmsSpread.getPaymentTime(), -cmsSpread.getPaymentTime() * discountFactorPayment));
final Map<String, List<DoublesPair>> resultMap = new HashMap<>();
resultMap.put(multicurves.getName(ccy), list);
final MulticurveSensitivity dfCurveSensitivity = MulticurveSensitivity.ofYieldDiscounting(resultMap);
MultipleCurrencyMulticurveSensitivity result = MultipleCurrencyMulticurveSensitivity.of(ccy, dfCurveSensitivity.multipliedBy(discountFactorPaymentBar));
result = result.plus(cmsCoupon1CurveSensitivity.multipliedBy(cmsCoupon1PvBar));
result = result.plus(cmsCoupon2CurveSensitivity.multipliedBy(cmsCoupon2PvBar));
result = result.plus(cmsCap1CurveSensitivity.multipliedBy(cmsCap1PvBar));
result = result.plus(cmsCap2CurveSensitivity.multipliedBy(cmsCap2PvBar));
//Calibration strike dependency -- START
result = result.plus(ccy, forward1CurveSensitivity.multipliedBy(strike1Bar));
result = result.plus(ccy, forward2CurveSensitivity.multipliedBy(strike2Bar));
//Calibration strike dependency -- END
return result;
}
/**
* Computes the present value sensitivity to the SABR parameters of a CMS spread cap/floor in the SABR framework.
* @param cmsSpread The CMS spread cap/floor.
* @param sabrData The SABR data bundle.
* @return The present value SABR sensitivity.
*/
public PresentValueSABRSensitivityDataBundle presentValueSABRSensitivity(final CapFloorCMSSpread cmsSpread, final SABRSwaptionProviderInterface sabrData) {
ArgumentChecker.notNull(cmsSpread, "CMA cap/floor");
ArgumentChecker.notNull(sabrData, "SABR swaption provider");
final Currency ccy = cmsSpread.getCurrency();
final MulticurveProviderInterface multicurves = sabrData.getMulticurveProvider();
// Forward sweep
final double forward1 = cmsSpread.getUnderlyingSwap1().accept(PRDC, multicurves);
final double forward2 = cmsSpread.getUnderlyingSwap2().accept(PRDC, multicurves);
CouponCMS cmsCoupon1 = CouponCMS.from(cmsSpread, cmsSpread.getUnderlyingSwap1(), cmsSpread.getSettlementTime());
cmsCoupon1 = cmsCoupon1.withNotional(Math.abs(cmsCoupon1.getNotional()));
CouponCMS cmsCoupon2 = CouponCMS.from(cmsSpread, cmsSpread.getUnderlyingSwap2(), cmsSpread.getSettlementTime());
cmsCoupon2 = cmsCoupon2.withNotional(Math.abs(cmsCoupon2.getNotional()));
final CapFloorCMS cmsCap1 = CapFloorCMS.from(cmsCoupon1, forward1, true);
final CapFloorCMS cmsCap2 = CapFloorCMS.from(cmsCoupon2, forward2, true);
final double cmsCoupon1Price = _methodCmsCoupon.presentValue(cmsCoupon1, sabrData).getAmount(ccy);
final double cmsCoupon2Price = _methodCmsCoupon.presentValue(cmsCoupon2, sabrData).getAmount(ccy);
final double cmsCap1Price = _methodCmsCap.presentValue(cmsCap1, sabrData).getAmount(ccy);
final double cmsCap2Price = _methodCmsCap.presentValue(cmsCap2, sabrData).getAmount(ccy);
final double discountFactorPayment = multicurves.getDiscountFactor(ccy, cmsSpread.getPaymentTime());
final double factor = discountFactorPayment * cmsCap1.getNotional() * cmsCap1.getPaymentYearFraction();
final double expectation1 = cmsCoupon1Price / factor;
final double expectation2 = cmsCoupon2Price / factor;
NormalFunctionData dataCap1 = new NormalFunctionData(expectation1, factor, 0.0);
final EuropeanVanillaOption optionCap1 = new EuropeanVanillaOption(forward1, cmsSpread.getFixingTime(), true);
double cmsCap1ImpliedVolatility = 0;
try {
cmsCap1ImpliedVolatility = NORMAL_IMPLIED_VOLATILITY.getImpliedVolatility(dataCap1, optionCap1, cmsCap1Price);
} catch (final Exception e) {
//TODO
}
NormalFunctionData dataCap2 = new NormalFunctionData(expectation2, factor, cmsCap1ImpliedVolatility);
final EuropeanVanillaOption optionCap2 = new EuropeanVanillaOption(forward2, cmsSpread.getFixingTime(), true);
double cmsCap2ImpliedVolatility = 0;
try {
cmsCap2ImpliedVolatility = NORMAL_IMPLIED_VOLATILITY.getImpliedVolatility(dataCap2, optionCap2, cmsCap2Price);
} catch (final Exception e) {
//TODO
}
final double rho = _correlation.evaluate(cmsSpread.getStrike());
final double cmsSpreadImpliedVolatility = Math.sqrt(cmsCap1ImpliedVolatility * cmsCap1ImpliedVolatility - 2 * rho * cmsCap1ImpliedVolatility * cmsCap2ImpliedVolatility + cmsCap2ImpliedVolatility
* cmsCap2ImpliedVolatility);
final NormalFunctionData dataSpread = new NormalFunctionData(expectation1 - expectation2, discountFactorPayment * cmsSpread.getNotional() * cmsSpread.getPaymentYearFraction(),
cmsSpreadImpliedVolatility);
final EuropeanVanillaOption optionSpread = new EuropeanVanillaOption(cmsSpread.getStrike(), cmsSpread.getFixingTime(), cmsSpread.isCap());
final double[] cmsSpreadPriceDerivative = new double[3];
NORMAL_PRICE.getPriceAdjoint(optionSpread, dataSpread, cmsSpreadPriceDerivative);
// Backward sweep
final double cmsSpreadPriceBar = 1.0;
final double cmsSpreadImpliedVolatilityBar = cmsSpreadPriceDerivative[1] * cmsSpreadPriceBar;
final double cmsCap2ImpliedVolatilityBar = (cmsCap2ImpliedVolatility - rho * cmsCap1ImpliedVolatility) / cmsSpreadImpliedVolatility * cmsSpreadImpliedVolatilityBar;
final double cmsCap1ImpliedVolatilityBar = (cmsCap1ImpliedVolatility - rho * cmsCap2ImpliedVolatility) / cmsSpreadImpliedVolatility * cmsSpreadImpliedVolatilityBar;
dataCap2 = new NormalFunctionData(expectation2, factor, cmsCap2ImpliedVolatility);
final double[] cmsCap2PriceNormalDerivative = new double[3];
NORMAL_PRICE.getPriceAdjoint(optionCap2, dataCap2, cmsCap2PriceNormalDerivative);
final double expectation2Bar = -cmsSpreadPriceDerivative[0] * cmsSpreadPriceBar + -cmsCap2PriceNormalDerivative[0] / cmsCap2PriceNormalDerivative[1] * cmsCap2ImpliedVolatilityBar;
dataCap1 = new NormalFunctionData(expectation1, factor, cmsCap1ImpliedVolatility);
final double[] cmsCap1PriceNormalDerivative = new double[3];
NORMAL_PRICE.getPriceAdjoint(optionCap1, dataCap1, cmsCap1PriceNormalDerivative);
final double expectation1Bar = cmsSpreadPriceDerivative[0] * cmsSpreadPriceBar + -cmsCap1PriceNormalDerivative[0] / cmsCap1PriceNormalDerivative[1] * cmsCap1ImpliedVolatilityBar;
final double cmsCap2PriceBar = 1.0 / cmsCap2PriceNormalDerivative[1] * cmsCap2ImpliedVolatilityBar;
final double cmsCap1PriceBar = 1.0 / cmsCap1PriceNormalDerivative[1] * cmsCap1ImpliedVolatilityBar;
final double cmsCoupon2PriceBar = expectation2Bar / factor;
final double cmsCoupon1PriceBar = expectation1Bar / factor;
PresentValueSABRSensitivityDataBundle cmsCoupon1SABRSensitivity = _methodCmsCoupon.presentValueSABRSensitivity(cmsCoupon1, sabrData);
PresentValueSABRSensitivityDataBundle cmsCoupon2SABRSensitivity = _methodCmsCoupon.presentValueSABRSensitivity(cmsCoupon2, sabrData);
PresentValueSABRSensitivityDataBundle cmsCap1SABRSensitivity = _methodCmsCap.presentValueSABRSensitivity(cmsCap1, sabrData);
PresentValueSABRSensitivityDataBundle cmsCap2SABRSensitivity = _methodCmsCap.presentValueSABRSensitivity(cmsCap2, sabrData);
cmsCoupon1SABRSensitivity = cmsCoupon1SABRSensitivity.multiplyBy(cmsCoupon1PriceBar);
cmsCoupon2SABRSensitivity = cmsCoupon2SABRSensitivity.multiplyBy(cmsCoupon2PriceBar);
cmsCap1SABRSensitivity = cmsCap1SABRSensitivity.multiplyBy(cmsCap1PriceBar);
cmsCap2SABRSensitivity = cmsCap2SABRSensitivity.multiplyBy(cmsCap2PriceBar);
PresentValueSABRSensitivityDataBundle result = cmsCoupon1SABRSensitivity;
result = result.plus(cmsCoupon2SABRSensitivity);
result = result.plus(cmsCap1SABRSensitivity);
result = result.plus(cmsCap2SABRSensitivity);
return result;
}
/**
* Inner class to solve for the implied correlation.
*/
class SolveCorrelation extends DoubleFunction1D {
/**
* The CMS spread cap/floor.
*/
private final CapFloorCMSSpread _cmsSpread;
/**
* The SABR data bundle.
*/
private final SABRSwaptionProviderInterface _sabrData;
/**
* The CMS spread price.
*/
private final double _price;
/**
* Constructor of the difference function.
* @param cmsSpread The CMS spread cap/floor.
* @param sabrData The SABR data bundle.
* @param price The CMS spread price.
*/
public SolveCorrelation(final CapFloorCMSSpread cmsSpread, final SABRSwaptionProviderInterface sabrData, final double price) {
this._cmsSpread = cmsSpread;
this._sabrData = sabrData;
this._price = price;
}
@Override
public Double evaluate(final Double x) {
@SuppressWarnings("synthetic-access")
final CapFloorCMSSpreadSABRBinormalMethod method = new CapFloorCMSSpreadSABRBinormalMethod(new RealPolynomialFunction1D(new double[] {x}), _methodCmsCap, _methodCmsCoupon);
return method.presentValue(_cmsSpread, _sabrData).getAmount(_cmsSpread.getCurrency()) - _price;
}
}
}