/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.montecarlo; import org.apache.commons.lang.Validate; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitorAdapter; import com.opengamma.analytics.financial.interestrate.annuity.derivative.AnnuityCouponIborRatchet; import com.opengamma.analytics.financial.interestrate.payments.derivative.CapFloorIbor; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIborGearing; import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIborRatchet; import com.opengamma.analytics.financial.interestrate.swaption.derivative.SwaptionPhysicalFixedIbor; /** * Computes the instrument price as the average over different paths. The data bundle contains the different discount factor paths and the instrument reference amounts. */ public class MonteCarloDiscountFactorCalculator extends InstrumentDerivativeVisitorAdapter<MonteCarloDiscountFactorDataBundle, Double> { /** * The unique instance of the calculator. */ private static final MonteCarloDiscountFactorCalculator INSTANCE = new MonteCarloDiscountFactorCalculator(); /** * Gets the calculator instance. * @return The calculator. */ public static MonteCarloDiscountFactorCalculator getInstance() { return INSTANCE; } /** * Constructor. */ MonteCarloDiscountFactorCalculator() { } @Override public Double visitCapFloorIbor(final CapFloorIbor payment, final MonteCarloDiscountFactorDataBundle mcResults) { final Double[][][] pathDiscountFactors = mcResults.getPathDiscountingFactor(); final double[][] impactAmount = mcResults.getImpactAmount(); Validate.isTrue(pathDiscountFactors[0].length == 1, "Only one decision date for cap/floor."); double price = 0; final int nbPath = pathDiscountFactors.length; double ibor; final double omega = (payment.isCap() ? 1.0 : -1.0); for (int looppath = 0; looppath < nbPath; looppath++) { ibor = (-impactAmount[0][0] * pathDiscountFactors[looppath][0][0] / (impactAmount[0][1] * pathDiscountFactors[looppath][0][1]) - 1.0) / payment.getFixingAccrualFactor(); price += Math.max(omega * (ibor - payment.getStrike()), 0) * pathDiscountFactors[looppath][0][2]; } price = price / nbPath * payment.getNotional() * payment.getPaymentYearFraction(); return price; } @Override public Double visitSwaptionPhysicalFixedIbor(final SwaptionPhysicalFixedIbor swaption, final MonteCarloDiscountFactorDataBundle mcResults) { final Double[][][] pathDiscountFactors = mcResults.getPathDiscountingFactor(); final double[][] impactAmount = mcResults.getImpactAmount(); Validate.isTrue(pathDiscountFactors[0].length == 1, "Only one decision date for swaptions."); double price = 0; final int nbPath = pathDiscountFactors.length; double swapPathValue; for (int looppath = 0; looppath < nbPath; looppath++) { swapPathValue = 0; for (int loopcf = 0; loopcf < impactAmount[0].length; loopcf++) { swapPathValue += impactAmount[0][loopcf] * pathDiscountFactors[looppath][0][loopcf]; } price += Math.max(swapPathValue, 0); } price = price / nbPath * (swaption.isLong() ? 1.0 : -1.0); return price; } @Override public Double visitAnnuityCouponIborRatchet(final AnnuityCouponIborRatchet annuity, final MonteCarloDiscountFactorDataBundle mcResults) { final Double[][][] pathDiscountFactors = mcResults.getPathDiscountingFactor(); final double[][] impactAmount = mcResults.getImpactAmount(); double price = 0.0; final int nbPath = pathDiscountFactors.length; final int nbCpn = annuity.getNumberOfPayments(); final double[] annuityPathValue = new double[nbPath]; final double[][] cpnRate = new double[nbCpn][nbPath]; double ibor; for (int loopcpn = 0; loopcpn < nbCpn; loopcpn++) { //nbCpn if (annuity.isFixed()[loopcpn]) { // Coupon already fixed: only one cash flow final CouponFixed cpn = (CouponFixed) annuity.getNthPayment(loopcpn); for (int looppath = 0; looppath < nbPath; looppath++) { cpnRate[loopcpn][looppath] = cpn.getFixedRate(); annuityPathValue[looppath] += impactAmount[loopcpn][0] * pathDiscountFactors[looppath][loopcpn][0]; } } else { if (annuity.getNthPayment(loopcpn) instanceof CouponIborRatchet) { final CouponIborRatchet cpn = (CouponIborRatchet) annuity.getNthPayment(loopcpn); for (int looppath = 0; looppath < nbPath; looppath++) { ibor = (-impactAmount[loopcpn][0] * pathDiscountFactors[looppath][loopcpn][0] / (impactAmount[loopcpn][1] * pathDiscountFactors[looppath][loopcpn][1]) - 1.0) / cpn.getFixingAccrualFactor(); final double cpnMain = cpn.getMainCoefficients()[0] * cpnRate[loopcpn - 1][looppath] + cpn.getMainCoefficients()[1] * ibor + cpn.getMainCoefficients()[2]; final double cpnFloor = cpn.getFloorCoefficients()[0] * cpnRate[loopcpn - 1][looppath] + cpn.getFloorCoefficients()[1] * ibor + cpn.getFloorCoefficients()[2]; final double cpnCap = cpn.getCapCoefficients()[0] * cpnRate[loopcpn - 1][looppath] + cpn.getCapCoefficients()[1] * ibor + cpn.getCapCoefficients()[2]; cpnRate[loopcpn][looppath] = Math.min(Math.max(cpnFloor, cpnMain), cpnCap); annuityPathValue[looppath] += cpnRate[loopcpn][looppath] * cpn.getPaymentYearFraction() * cpn.getNotional() * pathDiscountFactors[looppath][loopcpn][1]; } } else { final CouponIborGearing cpn = (CouponIborGearing) annuity.getNthPayment(loopcpn); for (int looppath = 0; looppath < nbPath; looppath++) { ibor = (-impactAmount[0][0] * pathDiscountFactors[looppath][0][0] / (impactAmount[0][1] * pathDiscountFactors[looppath][0][1]) - 1.0) / cpn.getFixingAccrualFactor(); cpnRate[loopcpn][looppath] = cpn.getFactor() * ibor + cpn.getSpread(); annuityPathValue[looppath] += cpnRate[loopcpn][looppath] * cpn.getPaymentYearFraction() * cpn.getNotional() * pathDiscountFactors[looppath][loopcpn][1]; } } } } for (int looppath = 0; looppath < nbPath; looppath++) { price += annuityPathValue[looppath]; } price = price / nbPath; return price; } }