Package com.opengamma.analytics.financial.montecarlo.provider

Source Code of com.opengamma.analytics.financial.montecarlo.provider.LiborMarketModelMonteCarloMethod

/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.montecarlo.provider;

import java.util.Arrays;

import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.model.interestrate.definition.LiborMarketModelDisplacedDiffusionParameters;
import com.opengamma.analytics.financial.montecarlo.DecisionSchedule;
import com.opengamma.analytics.financial.montecarlo.MonteCarloIborRateDataBundle;
import com.opengamma.analytics.financial.provider.description.interestrate.LiborMarketModelDisplacedDiffusionProvider;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderInterface;
import com.opengamma.analytics.math.matrix.CommonsMatrixAlgebra;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.math.matrix.MatrixAlgebra;
import com.opengamma.analytics.math.random.RandomNumberGenerator;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MultipleCurrencyAmount;

/**
* Monte Carlo pricing method in the Libor Market Model with Displaced Diffusion.
*/
public class LiborMarketModelMonteCarloMethod extends MonteCarloMethod {

  /**
   * The maximum length of a jump in the path generation.
   */
  private final double _maxJump;

  /**
   * The decision schedule calculator (calculate the exercise dates, the cash flow dates and the reference amounts).
   */
  private static final DecisionScheduleCalculator DC = DecisionScheduleCalculator.getInstance();
  /**
   * The calculator from discount factors (calculate the price from simulated discount factors and the reference amounts).
   */
  private static final MonteCarloIborRateCalculator MCC = MonteCarloIborRateCalculator.getInstance();
  /**
   * The number of paths in one block.
   */
  private static final int BLOCK_SIZE = 1000;
  /**
   * The default maximum length of a jump in the path generation.
   */
  private static final double MAX_JUMP_DEFAULT = 1.0;

  /**
   * Constructor.
   * @param numberGenerator The random number generator. Generate Normally distributed numbers.
   * @param nbPath The number of paths.
   */
  public LiborMarketModelMonteCarloMethod(final RandomNumberGenerator numberGenerator, final int nbPath) {
    super(numberGenerator, nbPath);
    _maxJump = MAX_JUMP_DEFAULT;
  }

  /**
   * Constructor.
   * @param numberGenerator The random number generator. Generate Normally distributed numbers.
   * @param nbPath The number of paths.
   * @param maxJump The maximum length of a jump in the path generation.
   */
  public LiborMarketModelMonteCarloMethod(final RandomNumberGenerator numberGenerator, final int nbPath, final double maxJump) {
    super(numberGenerator, nbPath);
    _maxJump = maxJump;
  }

  public MultipleCurrencyAmount presentValue(final InstrumentDerivative instrument, final Currency ccy, final LiborMarketModelDisplacedDiffusionProvider lmmData) {
    final MulticurveProviderInterface multicurves = lmmData.getMulticurveProvider();
    final LiborMarketModelDisplacedDiffusionParameters parameters = lmmData.getLMMParameters();
    // The numeraire is the last time in the LMM description.
    final DecisionSchedule decision = instrument.accept(DC, multicurves);
    final int[][] impactIndex = index(decision.getImpactTime(), parameters);

    final int nbPeriodLMM = parameters.getNbPeriod();
    final double[] initL = new double[nbPeriodLMM];
    final double[] deltaLMM = parameters.getAccrualFactor();
    final double[] dfL = new double[nbPeriodLMM + 1];
    for (int loopper = 0; loopper < nbPeriodLMM + 1; loopper++) {
      dfL[loopper] = multicurves.getDiscountFactor(ccy, parameters.getIborTime()[loopper]);
    }
    for (int loopper = 0; loopper < nbPeriodLMM; loopper++) {
      initL[loopper] = (dfL[loopper] / dfL[loopper + 1] - 1.0) / deltaLMM[loopper];
    }

    final int nbBlock = (int) Math.round(Math.ceil(getNbPath() / ((double) BLOCK_SIZE)));
    final int[] nbPath2 = new int[nbBlock];
    for (int i = 0; i < nbBlock - 1; i++) {
      nbPath2[i] = BLOCK_SIZE;
    }
    nbPath2[nbBlock - 1] = getNbPath() - (nbBlock - 1) * BLOCK_SIZE;

    double price = 0.0;
    for (int loopblock = 0; loopblock < nbBlock; loopblock++) {
      final double[][] initLPath = new double[nbPeriodLMM][nbPath2[loopblock]];
      for (int loopper = 0; loopper < nbPeriodLMM; loopper++) {
        for (int looppath = 0; looppath < nbPath2[loopblock]; looppath++) {
          initLPath[loopper][looppath] = initL[loopper];
        }
      }
      final double[][][] pathIbor = pathgeneratorlibor(decision.getDecisionTime(), initLPath, parameters);
      price += instrument.accept(MCC, new MonteCarloIborRateDataBundle(pathIbor, deltaLMM, decision.getImpactAmount(), impactIndex));
    }
    price *= multicurves.getDiscountFactor(ccy, parameters.getIborTime()[parameters.getIborTime().length - 1]) / getNbPath();
    return MultipleCurrencyAmount.of(ccy, price);
  }

  private int[][] index(final double[][] time, final LiborMarketModelDisplacedDiffusionParameters lmm) {
    final int[][] index = new int[time.length][];
    for (int loop1 = 0; loop1 < time.length; loop1++) {
      index[loop1] = new int[time[loop1].length];
      for (int loop2 = 0; loop2 < time[loop1].length; loop2++) {
        index[loop1][loop2] = lmm.getTimeIndex(time[loop1][loop2]);
      }
    }
    return index;
  }

  /**
   * Create one step in the LMM diffusion. The step is done through several jump times. The diffusion is approximated with a predictor-corrector approach.
   * @param jumpTime The jump times.
   * @param initIbor Rate at the start of the period. Size: nbPeriodLMM x nbPath.
   * @return The Ibor rates at the end of the jump period. Size: nbPeriodLMM x nbPath.
   */
  private double[][] stepPC(final double[] jumpTime, final double[][] initIbor, final LiborMarketModelDisplacedDiffusionParameters lmm) {
    final double amr = lmm.getMeanReversion();
    final double[] iborTime = lmm.getIborTime();
    final double[] almm = lmm.getDisplacement();
    final double[] deltalmm = lmm.getAccrualFactor();
    final DoubleMatrix2D gammaLMM = new DoubleMatrix2D(lmm.getVolatility());
    final MatrixAlgebra algebra = new CommonsMatrixAlgebra();
    final DoubleMatrix2D s = (DoubleMatrix2D) algebra.multiply(gammaLMM, algebra.getTranspose(gammaLMM));
    final int nbJump = jumpTime.length - 1;
    final int nbPath = initIbor[0].length;
    final int nbPeriodLMM = lmm.getNbPeriod();
    final int nbFactorLMM = lmm.getNbFactor();
    final double[] dt = new double[nbJump];
    final double[] alpha = new double[nbJump];
    final double[] alpha2 = new double[nbJump];
    for (int loopjump = 0; loopjump < nbJump; loopjump++) {
      dt[loopjump] = jumpTime[loopjump + 1] - jumpTime[loopjump];
      alpha[loopjump] = Math.exp(amr * jumpTime[loopjump + 1]);
      alpha2[loopjump] = alpha[loopjump] * alpha[loopjump];
    }

    final double[][] f = initIbor;
    for (int loopjump = 0; loopjump < nbJump; loopjump++) {
      final double sqrtDt = Math.sqrt(dt[loopjump]);
      int index = Arrays.binarySearch(iborTime, jumpTime[loopjump + 1] - lmm.getTimeTolerance());
      index = -index - 1; // The index from which the rate should be evolved.
      final int nI = nbPeriodLMM - index;
      final double[] dI = new double[nI];
      for (int loopn = 0; loopn < nI; loopn++) {
        dI[loopn] = 1.0 / deltalmm[index + loopn];
      }
      final double[][] salpha2Array = new double[nI][nI];
      for (int loopn1 = 0; loopn1 < nI; loopn1++) {
        for (int loopn2 = 0; loopn2 < nI; loopn2++) {
          salpha2Array[loopn1][loopn2] = s.getEntry(index + loopn1, index + loopn2) * alpha2[loopjump];
        }
      }
      final DoubleMatrix2D salpha2 = new DoubleMatrix2D(salpha2Array);
      // Random seed
      final double[][] dw = getNormalArray(nbFactorLMM, nbPath);
      // Common figures
      final double[] dr1 = new double[nI];
      for (int loopn = 0; loopn < nI; loopn++) {
        dr1[loopn] = -salpha2.getEntry(loopn, loopn) * dt[loopjump] / 2.0;
      }
      final double[][] cc = new double[nI][nbPath];
      for (int loopn = 0; loopn < nI; loopn++) {
        for (int looppath = 0; looppath < nbPath; looppath++) {
          for (int loopfact = 0; loopfact < nbFactorLMM; loopfact++) {
            cc[loopn][looppath] += gammaLMM.getEntry(index + loopn, loopfact) * dw[loopfact][looppath] * sqrtDt * alpha[loopjump];
          }
          cc[loopn][looppath] += dr1[loopn];
        }
      }
      // Unique step: predictor and corrector
      final double[][] mP = new double[nI][nbPath];
      final double[][] mC = new double[nI][nbPath];
      final double[][] coefP = new double[nbPath][nI - 1];
      final double[][] coefC = new double[nI][nbPath];
      for (int looppath = 0; looppath < nbPath; looppath++) {
        for (int loopn = 0; loopn < nI - 1; loopn++) {
          coefP[looppath][loopn] = (f[index + loopn + 1][looppath] + almm[index + loopn + 1]) / (f[index + loopn + 1][looppath] + dI[loopn + 1]);
        }
      }
      for (int loopdrift = nI - 1; loopdrift >= 0; loopdrift--) {
        if (loopdrift < nI - 1) {
          for (int looppath = 0; looppath < nbPath; looppath++) {
            coefC[loopdrift + 1][looppath] = (f[index + loopdrift + 1][looppath] + almm[index + loopdrift + 1]) / (f[index + loopdrift + 1][looppath] + dI[loopdrift + 1]);
            for (int loop = loopdrift + 1; loop < nI; loop++) {
              mP[loopdrift][looppath] += salpha2.getEntry(loop, loopdrift) * coefP[looppath][loop - 1];
              mC[loopdrift][looppath] += salpha2.getEntry(loop, loopdrift) * coefC[loop][looppath];
            }
          }
          for (int looppath = 0; looppath < nbPath; looppath++) {
            f[loopdrift + index][looppath] = (f[loopdrift + index][looppath] + almm[index + loopdrift])
                * Math.exp(-(mP[loopdrift][looppath] + mC[loopdrift][looppath]) * dt[loopjump] / 2.0 + cc[loopdrift][looppath]) - almm[index + loopdrift];
          }
        } else {
          for (int looppath = 0; looppath < nbPath; looppath++) {
            f[loopdrift + index][looppath] = (f[loopdrift + index][looppath] + almm[index + loopdrift]) * Math.exp(cc[loopdrift][looppath]) - almm[index + loopdrift];
          }
        }
      }
    }
    return f;
  }

  /**
   *
   * @param jumpTime The time of the mandatory jumps.
   * @param initIbor The Ibor rates at the start. nbPeriodLMM x nbPath
   * @param lmm The LMM parameters.
   * @return The paths. Size: nbJump x nbPeriodLMM x nbPath
   */
  private double[][][] pathgeneratorlibor(final double[] jumpTime, final double[][] initIbor, final LiborMarketModelDisplacedDiffusionParameters lmm) {
    final int nbPeriod = initIbor.length;
    final int nbPath = initIbor[0].length;
    final int nbJump = jumpTime.length;
    double[][] initTmp = new double[nbPeriod][nbPath];
    for (int loop1 = 0; loop1 < nbPeriod; loop1++) {
      System.arraycopy(initIbor[loop1], 0, initTmp[loop1], 0, nbPath);
    }
    final double[] jumpTimeA = new double[nbJump + 1];
    jumpTimeA[0] = 0;
    System.arraycopy(jumpTime, 0, jumpTimeA, 1, nbJump);
    final double[][][] result = new double[nbJump][nbPeriod][nbPath];
    // TODO: add intermediary jump dates if necessary
    for (int loopjump = 0; loopjump < nbJump; loopjump++) {
      // Intermediary jumps
      double[] jumpIn;
      if (jumpTimeA[loopjump + 1] - jumpTimeA[loopjump] < _maxJump) {
        jumpIn = new double[] {jumpTimeA[loopjump], jumpTimeA[loopjump + 1]};
      } else {
        final double jump = jumpTimeA[loopjump + 1] - jumpTimeA[loopjump];
        final int nbJumpIn = (int) Math.ceil(jump / _maxJump);
        jumpIn = new double[nbJumpIn + 1];
        jumpIn[0] = jumpTimeA[loopjump];
        for (int loopJumpIn = 1; loopJumpIn <= nbJumpIn; loopJumpIn++) {
          jumpIn[loopJumpIn] = jumpTimeA[loopjump] + loopJumpIn * jump / nbJumpIn;
        }
      }
      initTmp = stepPC(jumpIn, initTmp, lmm);
      for (int loop1 = 0; loop1 < nbPeriod; loop1++) {
        System.arraycopy(initTmp[loop1], 0, result[loopjump][loop1], 0, nbPath);
      }
    }
    return result;
  }

  /**
   * Gets a 2D-array of independent normally distributed variables.
   * @param nbJump The number of jumps.
   * @param nbPath The number of paths.
   * @return The array of variables.
   */
  private double[][] getNormalArray(final int nbJump, final int nbPath) {
    final double[][] result = new double[nbJump][nbPath];
    for (int loopjump = 0; loopjump < nbJump; loopjump++) {
      result[loopjump] = getNumberGenerator().getVector(nbPath);
    }
    return result;
  }

}
TOP

Related Classes of com.opengamma.analytics.financial.montecarlo.provider.LiborMarketModelMonteCarloMethod

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.