/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.payments.provider;
import com.opengamma.analytics.financial.interestrate.annuity.derivative.AnnuityPaymentFixed;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CapFloorCMS;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment;
import com.opengamma.analytics.financial.interestrate.swap.derivative.SwapFixedCoupon;
import com.opengamma.analytics.financial.model.interestrate.HullWhiteOneFactorPiecewiseConstantInterestRateModel;
import com.opengamma.analytics.financial.model.interestrate.definition.HullWhiteOneFactorPiecewiseConstantParameters;
import com.opengamma.analytics.financial.provider.calculator.discounting.CashFlowEquivalentCalculator;
import com.opengamma.analytics.financial.provider.description.interestrate.HullWhiteOneFactorProviderInterface;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderInterface;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;
/**
* Pricing method of a CMS coupon in the Hull-White (extended Vasicek) model by approximation.
* <P> Reference: M. Henrard. CMS Swaps and Caps in One-Factor Gaussian Models, SSRN working paper 985551, February 2008.
* Available at http://ssrn.com/abstract=985551
*/
public final class CapFloorCMSHullWhiteApproximationMethod {
/**
* The method unique instance.
*/
private static final CapFloorCMSHullWhiteApproximationMethod INSTANCE = new CapFloorCMSHullWhiteApproximationMethod();
/**
* Private constructor.
*/
private CapFloorCMSHullWhiteApproximationMethod() {
}
/**
* Return the unique instance of the class.
* @return The instance.
*/
public static CapFloorCMSHullWhiteApproximationMethod getInstance() {
return INSTANCE;
}
/**
* The model used in computations.
*/
private static final HullWhiteOneFactorPiecewiseConstantInterestRateModel MODEL = new HullWhiteOneFactorPiecewiseConstantInterestRateModel();
/**
* The cash flow equivalent calculator used in computations.
*/
private static final CashFlowEquivalentCalculator CFEC = CashFlowEquivalentCalculator.getInstance();
/**
* The normal distribution implementation.
*/
private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1);
/**
* Compute the present value of a CMS cap/floor with the Hull-White (extended Vasicek) model by approximation.
* @param cms The CMS cap/floor.
* @param hwMulticurves The Hull-White and multi-curves provider.
* @return The cap/floor price.
*/
public MultipleCurrencyAmount presentValue(final CapFloorCMS cms, final HullWhiteOneFactorProviderInterface hwMulticurves) {
ArgumentChecker.notNull(cms, "CMS");
ArgumentChecker.notNull(hwMulticurves, "Hull-White provider");
Currency ccy = cms.getCurrency();
HullWhiteOneFactorPiecewiseConstantParameters parameters = hwMulticurves.getHullWhiteParameters();
MulticurveProviderInterface multicurves = hwMulticurves.getMulticurveProvider();
final double expiryTime = cms.getFixingTime();
final SwapFixedCoupon<? extends Payment> swap = cms.getUnderlyingSwap();
final double dfPayment = multicurves.getDiscountFactor(ccy, cms.getPaymentTime());
final int nbFixed = cms.getUnderlyingSwap().getFixedLeg().getNumberOfPayments();
final double[] alphaFixed = new double[nbFixed];
final double[] dfFixed = new double[nbFixed];
final double[] discountedCashFlowFixed = new double[nbFixed];
for (int loopcf = 0; loopcf < nbFixed; loopcf++) {
alphaFixed[loopcf] = MODEL.alpha(parameters, 0.0, expiryTime, expiryTime, swap.getFixedLeg().getNthPayment(loopcf).getPaymentTime());
dfFixed[loopcf] = multicurves.getDiscountFactor(ccy, swap.getFixedLeg().getNthPayment(loopcf).getPaymentTime());
discountedCashFlowFixed[loopcf] = dfFixed[loopcf] * swap.getFixedLeg().getNthPayment(loopcf).getPaymentYearFraction() * swap.getFixedLeg().getNthPayment(loopcf).getNotional();
}
final AnnuityPaymentFixed cfeIbor = swap.getSecondLeg().accept(CFEC, multicurves);
final double[] alphaIbor = new double[cfeIbor.getNumberOfPayments()];
final double[] dfIbor = new double[cfeIbor.getNumberOfPayments()];
final double[] discountedCashFlowIbor = new double[cfeIbor.getNumberOfPayments()];
for (int loopcf = 0; loopcf < cfeIbor.getNumberOfPayments(); loopcf++) {
alphaIbor[loopcf] = MODEL.alpha(parameters, 0.0, expiryTime, expiryTime, cfeIbor.getNthPayment(loopcf).getPaymentTime());
dfIbor[loopcf] = multicurves.getDiscountFactor(ccy, cfeIbor.getNthPayment(loopcf).getPaymentTime());
discountedCashFlowIbor[loopcf] = dfIbor[loopcf] * cfeIbor.getNthPayment(loopcf).getAmount();
}
final double alphaPayment = MODEL.alpha(parameters, 0.0, expiryTime, expiryTime, cms.getPaymentTime());
final double x0 = -alphaPayment;
final double a0 = MODEL.swapRate(x0, discountedCashFlowFixed, alphaFixed, discountedCashFlowIbor, alphaIbor) - cms.getStrike();
final double a1 = MODEL.swapRateDx1(x0, discountedCashFlowFixed, alphaFixed, discountedCashFlowIbor, alphaIbor);
final double a2 = MODEL.swapRateDx2(x0, discountedCashFlowFixed, alphaFixed, discountedCashFlowIbor, alphaIbor);
// AnnuityPaymentFixed cfe = CFEC.visit(swap.withCoupon(cms.getStrike()), hwData);
// double[] alpha = new double[cfe.getNumberOfPayments()];
// double[] df = new double[cfe.getNumberOfPayments()];
// double[] discountedCashFlow = new double[cfe.getNumberOfPayments()];
// for (int loopcf = 0; loopcf < cfe.getNumberOfPayments(); loopcf++) {
// alpha[loopcf] = MODEL.alpha(hwData.getHullWhiteParameter(), 0.0, expiryTime, expiryTime, cfe.getNthPayment(loopcf).getPaymentTime());
// df[loopcf] = hwData.getCurve(cfe.getDiscountCurve()).getDiscountFactor(cfe.getNthPayment(loopcf).getPaymentTime());
// discountedCashFlow[loopcf] = df[loopcf] * cfe.getNthPayment(loopcf).getAmount();
// }
// double kappaTest = MODEL.kappa(discountedCashFlow, alpha);
final double kappa = -a0 / a1 - alphaPayment; // approximation
final double kappatilde = kappa + alphaPayment;
final double omega = (cms.isCap() ? 1.0 : -1.0);
final double s2pi = 1.0 / Math.sqrt(2.0 * Math.PI);
double pv = omega * (a0 + a2 / 2) * NORMAL.getCDF(-omega * kappatilde) + s2pi * Math.exp(-kappatilde * kappatilde / 2) * (a1 + a2 * kappatilde);
pv *= dfPayment * cms.getNotional() * cms.getPaymentYearFraction();
return MultipleCurrencyAmount.of(cms.getCurrency(), pv);
}
}