Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* (c) Copyright Christian P. Fries, Germany. Contact: [email protected].
*
* Created on 09.02.2004
*/
package net.finmath.montecarlo.interestrate.models;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.finmath.exception.CalculationException;
import net.finmath.marketdata.model.AnalyticModel;
import net.finmath.marketdata.model.curves.DiscountCurve;
import net.finmath.marketdata.model.curves.ForwardCurve;
import net.finmath.montecarlo.RandomVariableFactory;
import net.finmath.montecarlo.RandomVariableFromArrayFactory;
import net.finmath.montecarlo.interestrate.CalibrationProduct;
import net.finmath.montecarlo.interestrate.TermStructureModel;
import net.finmath.montecarlo.interestrate.models.covariance.TermStructureCovarianceModel;
import net.finmath.montecarlo.interestrate.models.covariance.TermStructureCovarianceModelParametric;
import net.finmath.montecarlo.model.AbstractProcessModel;
import net.finmath.montecarlo.process.MonteCarloProcess;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretization;
import net.finmath.time.TimeDiscretizationFromArray;
/**
* Implements a discretized Heath-Jarrow-Morton model / LIBOR market model with dynamic tenor refinement, see
* https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2884699.
*
*
* In its default case the class specifies a multi-factor LIBOR market model, that is
* \( L_{j} = \frac{1}{T_{j+1}-T_{j}} ( exp(Y_{j}) - 1 ) \), where
* \[
* dY_{j} = \mu_{j} dt + \lambda_{1,j} dW_{1} + \ldots + \lambda_{m,j} dW_{m}
* \]
*
* The model uses an AbstractLIBORCovarianceModel for the specification of
* (λ1,j,...,λm,j) as a covariance model.
* See {@link net.finmath.montecarlo.model.ProcessModel} for details on the implemented interface
*
* The model uses an AbstractLIBORCovarianceModel as a covariance model.
* If the covariance model is of type AbstractLIBORCovarianceModelParametric
* a calibration to swaptions can be performed.
*
* Note that λ may still depend on L (through a local volatility model).
*
* The simulation is performed under spot measure, that is, the numeraire
* is \( N(T_{i}) = \prod_{j=0}^{i-1} (1 + L(T_{j},T_{j+1};T_{j}) (T_{j+1}-T_{j})) \).
*
* The map properties allows to configure the model. The following keys may be used:
*
*
* liborCap: An optional Double value applied as a cap to the LIBOR rates.
* May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and
* numerical problems. To disable the cap, set liborCap to Double.POSITIVE_INFINITY.
*
*
*
*
* The main task of this class is to calculate the risk-neutral drift and the
* corresponding numeraire given the covariance model.
*
* The calibration of the covariance structure is not part of this class.
*
* @author Christian Fries
* @version 1.2
* @see net.finmath.montecarlo.process.MonteCarloProcess The interface for numerical schemes.
* @see net.finmath.montecarlo.model.ProcessModel The interface for models provinding parameters to numerical schemes.
* @see https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2884699
*/
public class LIBORMarketModelWithTenorRefinement extends AbstractProcessModel implements TermStructureModel {
public enum Driftapproximation { EULER, LINE_INTEGRAL, PREDICTOR_CORRECTOR }
private final TimeDiscretization[] liborPeriodDiscretizations;
private final Integer[] numberOfDiscretizationIntervals;
private final AnalyticModel curveModel;
private final ForwardCurve forwardRateCurve;
private final DiscountCurve discountCurve;
private final RandomVariableFactory randomVariableFactory = new RandomVariableFromArrayFactory();
private TermStructureCovarianceModel covarianceModel;
// Cache for the numeraires, needs to be invalidated if process changes
private final ConcurrentHashMap numeraires;
private MonteCarloProcess numerairesProcess = null;
/**
* Creates a model for given covariance.
*
* Creates a discretized Heath-Jarrow-Morton model / LIBOR market model with dynamic tenor refinement, see
* https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2884699.
*
* If calibrationItems in non-empty and the covariance model is a parametric model,
* the covariance will be replaced by a calibrate version of the same model, i.e.,
* the LIBOR Market Model will be calibrated.
*
* The map properties allows to configure the model. The following keys may be used:
*
*
* liborCap: An optional Double value applied as a cap to the LIBOR rates.
* May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and
* numerical problems. To disable the cap, set liborCap to Double.POSITIVE_INFINITY.
*
*
* calibrationParameters: Possible values:
*
*
* Map<String,Object> a parameter map with the following key/value pairs:
*
*
* accuracy: Double specifying the required solver accuracy.
*
*
* maxIterations: Integer specifying the maximum iterations for the solver.
*
*
*
*
*
*
*
* @param liborPeriodDiscretizations A vector of tenor discretizations of the interest rate curve into forward rates (tenor structure), finest first.
* @param numberOfDiscretizationIntervals A vector of number of periods to be taken from the liborPeriodDiscretizations.
* @param analyticModel The associated analytic model of this model (containing the associated market data objects like curve).
* @param forwardRateCurve The initial values for the forward rates.
* @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment.
* @param covarianceModel The covariance model to use.
* @param calibrationProducts The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i).
* @param properties Key value map specifying properties like measure and stateSpace.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the cause() method.
*/
public LIBORMarketModelWithTenorRefinement(
final TimeDiscretization[] liborPeriodDiscretizations,
final Integer[] numberOfDiscretizationIntervals,
final AnalyticModel analyticModel,
final ForwardCurve forwardRateCurve,
final DiscountCurve discountCurve,
final TermStructureCovarianceModel covarianceModel,
final CalibrationProduct[] calibrationProducts,
final Map properties
) throws CalculationException {
Map calibrationParameters = null;
if(properties != null && properties.containsKey("calibrationParameters")) {
@SuppressWarnings("unchecked")
Map calibrationParametersProperty = (Map)properties.get("calibrationParameters");
calibrationParameters = calibrationParametersProperty;
}
this.liborPeriodDiscretizations = liborPeriodDiscretizations;
this.numberOfDiscretizationIntervals = numberOfDiscretizationIntervals;
curveModel = analyticModel;
this.forwardRateCurve = forwardRateCurve;
this.discountCurve = discountCurve;
this.covarianceModel = covarianceModel;
// Perform calibration, if data is given
if(calibrationProducts != null && calibrationProducts.length > 0) {
TermStructureCovarianceModelParametric covarianceModelParametric = null;
try {
covarianceModelParametric = (TermStructureCovarianceModelParametric)covarianceModel;
}
catch(final Exception e) {
throw new ClassCastException("Calibration is currently restricted to parametric covariance models (TermStructureCovarianceModelParametricInterface).");
}
this.covarianceModel = covarianceModelParametric.getCloneCalibrated(this, calibrationProducts, calibrationParameters);
}
numeraires = new ConcurrentHashMap<>();
}
/**
* Return the numeraire at a given time.
*
* The numeraire is provided for interpolated points. If requested on points which are not
* part of the tenor discretization, the numeraire uses a linear interpolation of the reciprocal
* value. See ISBN 0470047224 for details.
*
* @param time Time time t for which the numeraire should be returned N(t).
* @return The numeraire at the specified time as RandomVariable
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the cause() method.
*/
@Override
public RandomVariable getNumeraire(final MonteCarloProcess process, final double time) throws CalculationException {
final int timeIndex = liborPeriodDiscretizations[0].getTimeIndex(time);
final TimeDiscretization liborPeriodDiscretization = liborPeriodDiscretizations[0];
if(timeIndex < 0) {
// Interpolation of Numeraire: log linear interpolation.
final int upperIndex = -timeIndex-1;
final int lowerIndex = upperIndex-1;
if(lowerIndex < 0) {
throw new IllegalArgumentException("Numeraire requested for time " + time + ". Unsupported");
}
final double alpha = (time-liborPeriodDiscretization.getTime(lowerIndex)) / (liborPeriodDiscretization.getTime(upperIndex) - liborPeriodDiscretization.getTime(lowerIndex));
RandomVariable numeraire = getNumeraire(process, liborPeriodDiscretization.getTime(upperIndex)).log().mult(alpha).add(getNumeraire(process, liborPeriodDiscretization.getTime(lowerIndex)).log().mult(1.0-alpha)).exp();
/*
* Adjust for discounting, i.e. funding or collateralization
*/
if(discountCurve != null) {
// This includes a control for zero bonds
final double deterministicNumeraireAdjustment = numeraire.invert().getAverage() / discountCurve.getDiscountFactor(curveModel, time);
numeraire = numeraire.mult(deterministicNumeraireAdjustment);
}
return numeraire;
}
/*
* Calculate the numeraire, when time is part of liborPeriodDiscretization
*/
/*
* Check if numeraire cache is values (i.e. process did not change)
*/
if(process != numerairesProcess) {
numeraires.clear();
numerairesProcess = process;
}
/*
* Check if numeraire is part of the cache
*/
RandomVariable numeraire = numeraires.get(timeIndex);
if(numeraire == null) {
/*
* Calculate the numeraire for timeIndex
*/
if(timeIndex == 0) {
numeraire = process.getStochasticDriver().getRandomVariableForConstant(1.0);
}
else {
// Initialize to previous numeraire
numeraire = getNumeraire(process, liborPeriodDiscretizations[0].getTime(timeIndex-1));
final double periodStart = liborPeriodDiscretizations[0].getTime(timeIndex-1);
final double periodEnd = liborPeriodDiscretizations[0].getTime(timeIndex);
final RandomVariable libor = getForwardRate(process, periodStart, periodStart, periodEnd);
numeraire = numeraire.accrue(libor, periodEnd-periodStart);
}
// Cache the numeraire
numeraires.put(timeIndex, numeraire);
}
/*
* Adjust for discounting, i.e. funding or collateralization
*/
if(discountCurve != null) {
// This includes a control for zero bonds
final double deterministicNumeraireAdjustment = numeraire.invert().getAverage() / discountCurve.getDiscountFactor(curveModel, time);
numeraire = numeraire.mult(deterministicNumeraireAdjustment);
}
return numeraire;
}
@Override
public RandomVariable[] getInitialState(MonteCarloProcess process) {
final RandomVariable[] initialStateRandomVariable = new RandomVariable[getNumberOfComponents()];
for(int componentIndex=0; componentIndexMeasure.SPOT or Measure.TERMINAL - depending how the
* model object was constructed. For Measure.TERMINAL the j-th entry of the return value is the random variable
* \[
* \mu_{j}^{\mathbb{Q}^{P(T_{n})}}(t) \ = \ - \mathop{\sum_{l\geq j+1}}_{l\leq n-1} \frac{\delta_{l}}{1+\delta_{l} L_{l}(t)} (\lambda_{j}(t) \cdot \lambda_{l}(t))
* \]
* and for Measure.SPOT the j-th entry of the return value is the random variable
* \[
* \mu_{j}^{\mathbb{Q}^{N}}(t) \ = \ \sum_{m(t) < l\leq j} \frac{\delta_{l}}{1+\delta_{l} L_{l}(t)} (\lambda_{j}(t) \cdot \lambda_{l}(t))
* \]
* where \( \lambda_{j} \) is the vector for factor loadings for the j-th component of the stochastic process (that is, the diffusion part is
* \( \sum_{k=1}^m \lambda_{j,k} \mathrm{d}W_{k} \)).
*
* Note: The scalar product of the factor loadings determines the instantaneous covariance. If the model is written in log-coordinates (using exp as a state space transform), we find
* \(\lambda_{j} \cdot \lambda_{l} = \sum_{k=1}^m \lambda_{j,k} \lambda_{l,k} = \sigma_{j} \sigma_{l} \rho_{j,l} \).
* If the model is written without a state space transformation (in its orignial coordinates) then \(\lambda_{j} \cdot \lambda_{l} = \sum_{k=1}^m \lambda_{j,k} \lambda_{l,k} = L_{j} L_{l} \sigma_{j} \sigma_{l} \rho_{j,l} \).
*
*
* @see net.finmath.montecarlo.interestrate.models.LIBORMarketModelWithTenorRefinement#getNumeraire(MonteCarloProcess, double) The calculation of the drift is consistent with the calculation of the numeraire in getNumeraire.
* @see net.finmath.montecarlo.interestrate.models.LIBORMarketModelWithTenorRefinement#getFactorLoading(MonteCarloProcess, int, int, RandomVariable[]) The factor loading \( \lambda_{j,k} \).
*
* @param process The discretization process generating this model. The process provides call backs for TimeDiscretization and allows calls to getProcessValue for timeIndices less or equal the given one.
* @param timeIndex Time index i for which the drift should be returned μ(ti).
* @param realizationAtTimeIndex Time current forward rate vector at time index i which should be used in the calculation.
* @return The drift vector μ(ti) as RandomVariableFromDoubleArray[]
*/
@Override
public RandomVariable[] getDrift(final MonteCarloProcess process, final int timeIndex, final RandomVariable[] realizationAtTimeIndex, final RandomVariable[] realizationPredictor) {
final double time = process.getTime(timeIndex);
final double timeStep = process.getTimeDiscretization().getTimeStep(timeIndex);
final double timeNext = process.getTime(timeIndex+1);
final RandomVariable zero = process.getStochasticDriver().getRandomVariableForConstant(0.0);
// Allocate drift vector and initialize to zero (will be used to sum up drift components)
final RandomVariable[] drift = new RandomVariable[getNumberOfComponents()];
for(int componentIndex=0; componentIndex 0) {
return weight2 / covarianceModel.getScaledTenorTime(periodStart, periodEnd) - weight1 / covarianceModel.getScaledTenorTime(periodStartPrevious, periodEndPrevious);
} else {
return weight2 / covarianceModel.getScaledTenorTime(periodStart, periodEnd);
}
}
@Override
public RandomVariable[] getFactorLoading(final MonteCarloProcess process, final int timeIndex, final int componentIndex, final RandomVariable[] realizationAtTimeIndex)
{
final RandomVariable zero = process.getStochasticDriver().getRandomVariableForConstant(0.0);
if(componentIndex < getNumberOfLibors()) {
final TimeDiscretization liborPeriodDiscretization = getLiborPeriodDiscretization(process.getTime(timeIndex));
final TimeDiscretization liborPeriodDiscretizationNext = getLiborPeriodDiscretization(process.getTime(timeIndex+1));
final double periodStart = liborPeriodDiscretizationNext.getTime(componentIndex);
final double periodEnd = liborPeriodDiscretizationNext.getTime(componentIndex+1);
final RandomVariable[] factorLoadingVector = covarianceModel.getFactorLoading(process.getTime(timeIndex), periodStart, periodEnd, liborPeriodDiscretization, realizationAtTimeIndex, this);
return factorLoadingVector;
}
else {
final RandomVariable[] zeros = new RandomVariable[process.getStochasticDriver().getNumberOfFactors()];
Arrays.fill(zeros, zero);
return zeros;
}
}
@Override
public RandomVariable applyStateSpaceTransform(final MonteCarloProcess process, final int timeIndex, final int componentIndex, final RandomVariable randomVariable) {
return randomVariable;
}
@Override
public RandomVariable applyStateSpaceTransformInverse(final MonteCarloProcess process, final int timeIndex, final int componentIndex, final RandomVariable randomVariable) {
return randomVariable;
}
@Override
public RandomVariable getRandomVariableForConstant(final double value) {
return randomVariableFactory.createRandomVariable(value);
}
private TimeDiscretization getLiborPeriodDiscretization(final double time) {
final ArrayList tenorTimes = new ArrayList<>();
final double firstTime = liborPeriodDiscretizations[0].getTime(liborPeriodDiscretizations[0].getTimeIndexNearestLessOrEqual(time));
double lastTime = firstTime;
tenorTimes.add(firstTime);
for(int discretizationLevelIndex = 0; discretizationLevelIndex= liborPeriodDiscretizations[discretizationLevelIndex].getNumberOfTimes()) {
break;
}
lastTime = liborPeriodDiscretizations[discretizationLevelIndex].getTime(tentorIntervalStartIndex+tenorInterval);
// round to liborPeriodDiscretizations[0]
lastTime = liborPeriodDiscretizations[0].getTime(liborPeriodDiscretizations[0].getTimeIndexNearestLessOrEqual(lastTime));
tenorTimes.add(lastTime);
}
}
return new TimeDiscretizationFromArray(tenorTimes);
}
public RandomVariable getStateVariableForPeriod(final TimeDiscretization liborPeriodDiscretization, final RandomVariable[] stateVariables, final double periodStart, final double periodEnd) {
int periodStartIndex = liborPeriodDiscretization.getTimeIndex(periodStart);
int periodEndIndex = liborPeriodDiscretization.getTimeIndex(periodEnd);
RandomVariable stateVariableSum = randomVariableFactory.createRandomVariable(0.0);
if(periodStartIndex < 0) {
periodStartIndex = -periodStartIndex-1;
if(periodStartIndex >= liborPeriodDiscretization.getNumberOfTimes()) {
throw new IllegalArgumentException();
}
final RandomVariable stateVariable = stateVariables[periodStartIndex-1];
final double shortPeriodEnd = liborPeriodDiscretization.getTime(periodStartIndex);
final double tenorRefinementWeight = getWeightForTenorRefinement(liborPeriodDiscretization.getTime(periodStartIndex-1), shortPeriodEnd, periodStart, shortPeriodEnd);
final RandomVariable integratedVariance = stateVariables[getNumberOfLibors()+periodStartIndex-1];
final double tenor = covarianceModel.getScaledTenorTime(periodStart, shortPeriodEnd);
stateVariableSum = stateVariableSum.addProduct(stateVariable.addProduct(integratedVariance, tenorRefinementWeight), tenor);
}
if(periodEndIndex < 0) {
periodEndIndex = -periodEndIndex-1;
final RandomVariable stateVariable = stateVariables[periodEndIndex-1];
final double shortPeriodStart = liborPeriodDiscretization.getTime(periodEndIndex-1);
final double tenorRefinementWeight = getWeightForTenorRefinement(shortPeriodStart, liborPeriodDiscretization.getTime(periodEndIndex), shortPeriodStart, periodEnd);
final RandomVariable integratedVariance = stateVariables[getNumberOfLibors()+periodEndIndex-1];
final double tenor = covarianceModel.getScaledTenorTime(shortPeriodStart, periodEnd);
stateVariableSum = stateVariableSum.addProduct(stateVariable.addProduct(integratedVariance, tenorRefinementWeight), tenor);
periodEndIndex--;
}
for(int periodIndex = periodStartIndex; periodIndex= liborPeriodDiscretization.getNumberOfTimes()) {
throw new IllegalArgumentException();
}
final RandomVariable stateVariable = process.getProcessValue(timeIndex, periodStartIndex-1);
final double shortPeriodEnd = liborPeriodDiscretization.getTime(periodStartIndex);
final double tenorRefinementWeight = getWeightForTenorRefinement(liborPeriodDiscretization.getTime(periodStartIndex-1), shortPeriodEnd, periodStart, shortPeriodEnd);
final RandomVariable integratedVariance = getIntegratedVariance(process, timeIndex, liborPeriodDiscretization.getTime(periodStartIndex-1), liborPeriodDiscretization.getTime(periodStartIndex));
stateVariableSum = stateVariableSum.addProduct(stateVariable.addProduct(integratedVariance, tenorRefinementWeight), covarianceModel.getScaledTenorTime(periodStart, shortPeriodEnd));
}
if(periodEndIndex < 0) {
periodEndIndex = -periodEndIndex-1;
final RandomVariable stateVariable = process.getProcessValue(timeIndex, periodEndIndex-1);
final double shortPeriodStart = liborPeriodDiscretization.getTime(periodEndIndex-1);
final double tenorRefinementWeight = getWeightForTenorRefinement(shortPeriodStart, liborPeriodDiscretization.getTime(periodEndIndex), shortPeriodStart, periodEnd);
final RandomVariable integratedVariance = getIntegratedVariance(process, timeIndex, liborPeriodDiscretization.getTime(periodEndIndex-1), liborPeriodDiscretization.getTime(periodEndIndex));
stateVariableSum = stateVariableSum.addProduct(stateVariable.addProduct(integratedVariance, tenorRefinementWeight), covarianceModel.getScaledTenorTime(shortPeriodStart,periodEnd));
periodEndIndex--;
}
for(int periodIndex = periodStartIndex; periodIndex properties = new HashMap();
properties.put("measure", measure.name());
properties.put("stateSpace", stateSpace.name());
return new LIBORMarketModelWithTenorRefinement(getLiborPeriodDiscretization(), getForwardRateCurve(), getDiscountCurve(), covarianceModel, new CalibrationProduct[0], properties);
} catch (CalculationException e) {
return null;
}
*/
}
@Override
public AnalyticModel getAnalyticModel() {
return curveModel;
}
@Override
public DiscountCurve getDiscountCurve() {
return discountCurve;
}
@Override
public ForwardCurve getForwardRateCurve() {
return forwardRateCurve;
}
@Override
public TermStructureModel getCloneWithModifiedData(final Map dataModified) throws CalculationException {
final CalibrationProduct[] calibrationItems = null;
final Map properties = null;
TermStructureCovarianceModel covarianceModel = this.covarianceModel;
if(dataModified.containsKey("covarianceModel")) {
covarianceModel = (TermStructureCovarianceModel)dataModified.get("covarianceModel");
}
return new LIBORMarketModelWithTenorRefinement(liborPeriodDiscretizations, numberOfDiscretizationIntervals, curveModel, forwardRateCurve, discountCurve, covarianceModel, calibrationItems, properties);
}
/**
* Returns the term structure covariance model.
*
* @return the term structure covariance model.
*/
public TermStructureCovarianceModel getCovarianceModel() {
return covarianceModel;
}
}