/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.interestrate.future.provider; import org.apache.commons.math.stat.descriptive.rank.Min; import com.opengamma.analytics.financial.interestrate.annuity.derivative.AnnuityPaymentFixed; import com.opengamma.analytics.financial.interestrate.future.derivative.BondFuturesSecurity; import com.opengamma.analytics.financial.interestrate.payments.derivative.PaymentFixed; import com.opengamma.analytics.financial.legalentity.LegalEntity; import com.opengamma.analytics.financial.model.interestrate.HullWhiteOneFactorPiecewiseConstantInterestRateModel; import com.opengamma.analytics.financial.provider.calculator.discounting.CashFlowEquivalentCalculator; import com.opengamma.analytics.financial.provider.description.interestrate.HullWhiteIssuerProviderInterface; import com.opengamma.analytics.math.function.Function1D; import com.opengamma.analytics.math.integration.RungeKuttaIntegrator1D; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; /** * Method to compute the bond futures security results with the price computed as the cheapest forward. */ public final class BondFuturesSecurityHullWhiteNumericalIntegrationMethod { /** * Creates the method unique instance. */ private static final BondFuturesSecurityHullWhiteNumericalIntegrationMethod INSTANCE = new BondFuturesSecurityHullWhiteNumericalIntegrationMethod(); /** * Return the method unique instance. * @return The instance. */ public static BondFuturesSecurityHullWhiteNumericalIntegrationMethod getInstance() { return INSTANCE; } /** * Constructor. */ private BondFuturesSecurityHullWhiteNumericalIntegrationMethod() { } /** * 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(); /** * Minimal number of integration steps in the replication. */ private static final int NB_INTEGRATION = 15; /** * Function to compute the minimum of an array. */ private static final Min MIN_FUNCTION = new Min(); /** * Computes the future price from the curves used to price the underlying bonds and a Hull-White one factor model. Computation by numerical integration. * @param futures The future security. * @param data The curve and Hull-White parameters. * @return The future price. */ public double price(final BondFuturesSecurity futures, final HullWhiteIssuerProviderInterface data) { ArgumentChecker.notNull(futures, "Futures"); ArgumentChecker.notNull(data, "Hull-White/Issuer provider"); final Currency ccy = futures.getCurrency(); final LegalEntity issuer = futures.getDeliveryBasketAtDeliveryDate()[0].getIssuerEntity(); final double expiryTime = futures.getNoticeLastTime(); final double deliveryTime = futures.getDeliveryLastTime(); final int nbBonds = futures.getDeliveryBasketAtDeliveryDate().length; final int[] nbPayments = new int[nbBonds]; final AnnuityPaymentFixed[] cfe = new AnnuityPaymentFixed[nbBonds]; for (int loopb = 0; loopb < nbBonds; loopb++) { cfe[loopb] = futures.getDeliveryBasketAtDeliveryDate()[loopb].accept(CFEC, data.getMulticurveProvider()); nbPayments[loopb] = cfe[loopb].getNumberOfPayments(); final PaymentFixed[] payments = new PaymentFixed[nbPayments[loopb] + 1]; payments[0] = new PaymentFixed(ccy, deliveryTime, -futures.getDeliveryBasketAtDeliveryDate()[loopb].getAccruedInterest()); System.arraycopy(cfe[loopb].getPayments(), 0, payments, 1, nbPayments[loopb]); cfe[loopb] = new AnnuityPaymentFixed(payments); } final double[][] alpha = new double[nbBonds][]; final double[][] beta = new double[nbBonds][]; final double[][] df = new double[nbBonds][]; final double[][] discountedCashFlow = new double[nbBonds][]; for (int loopb = 0; loopb < nbBonds; loopb++) { alpha[loopb] = new double[nbPayments[loopb] + 1]; beta[loopb] = new double[nbPayments[loopb] + 1]; df[loopb] = new double[nbPayments[loopb] + 1]; discountedCashFlow[loopb] = new double[nbPayments[loopb] + 1]; for (int loopcf = 0; loopcf < cfe[loopb].getNumberOfPayments(); loopcf++) { alpha[loopb][loopcf] = MODEL.alpha(data.getHullWhiteParameters(), 0.0, expiryTime, deliveryTime, cfe[loopb].getNthPayment(loopcf).getPaymentTime()); beta[loopb][loopcf] = MODEL.futuresConvexityFactor(data.getHullWhiteParameters(), expiryTime, cfe[loopb].getNthPayment(loopcf).getPaymentTime(), deliveryTime); df[loopb][loopcf] = data.getIssuerProvider().getDiscountFactor(issuer, cfe[loopb].getNthPayment(loopcf).getPaymentTime()); discountedCashFlow[loopb][loopcf] = df[loopb][loopcf] / df[loopb][0] * cfe[loopb].getNthPayment(loopcf).getAmount() * beta[loopb][loopcf] / futures.getConversionFactor()[loopb]; } } // Integration final FuturesIntegrant integrant = new FuturesIntegrant(discountedCashFlow, alpha); final double limit = 10.0; final double absoluteTolerance = 1.0E-2; final double relativeTolerance = 1.0E-6; final RungeKuttaIntegrator1D integrator = new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, NB_INTEGRATION); double price = 0.0; try { price = 1.0 / Math.sqrt(2.0 * Math.PI) * integrator.integrate(integrant, -limit, limit); } catch (final Exception e) { throw new RuntimeException(e); } return price; } /** * Inner class to implement the integration used in price replication. */ private static final class FuturesIntegrant extends Function1D<Double, Double> { private final double[][] _discountedCashFlow; private final double[][] _alpha; private final int _nbBonds; /** * Constructor to the integrant function. * @param discountedCashFlow The discounted cash flows. * @param alpha The bond volatilities. */ public FuturesIntegrant(final double[][] discountedCashFlow, final double[][] alpha) { _discountedCashFlow = discountedCashFlow; _alpha = alpha; _nbBonds = discountedCashFlow.length; } @Override public Double evaluate(final Double x) { double[] bond = new double[_nbBonds]; for (int loopb = 0; loopb < _nbBonds; loopb++) { for (int loopcf = 0; loopcf < _discountedCashFlow[loopb].length; loopcf++) { bond[loopb] += _discountedCashFlow[loopb][loopcf] * Math.exp(-(x + _alpha[loopb][loopcf]) * (x + _alpha[loopb][loopcf]) / 2.0); } } return MIN_FUNCTION.evaluate(bond); } } }