/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de.
*
* Created on 09.02.2004
*/
package net.finmath.montecarlo.interestrate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.finmath.exception.CalculationException;
import net.finmath.functions.AnalyticFormulas;
import net.finmath.marketdata.model.AnalyticModelInterface;
import net.finmath.marketdata.model.curves.DiscountCurveFromForwardCurve;
import net.finmath.marketdata.model.curves.DiscountCurveInterface;
import net.finmath.marketdata.model.curves.ForwardCurveInterface;
import net.finmath.marketdata.model.volatilities.AbstractSwaptionMarketData;
import net.finmath.marketdata.products.Swap;
import net.finmath.marketdata.products.SwapAnnuity;
import net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModel;
import net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModelParametric;
import net.finmath.montecarlo.interestrate.products.AbstractLIBORMonteCarloProduct;
import net.finmath.montecarlo.interestrate.products.SwaptionAnalyticApproximation;
import net.finmath.montecarlo.interestrate.products.SwaptionSimple;
import net.finmath.montecarlo.model.AbstractModel;
import net.finmath.montecarlo.process.AbstractProcessInterface;
import net.finmath.stochastic.RandomVariableInterface;
import net.finmath.time.RegularSchedule;
import net.finmath.time.ScheduleInterface;
import net.finmath.time.TimeDiscretization;
import net.finmath.time.TimeDiscretizationInterface;
/**
* Implements a (generalized) LIBOR market model with some drift approximation methods.
* <br><br>
* In its default case the class specifies a multi-factor LIBOR market model in its log-normal formulation, that is
* <i>L<sub>j</sub> = exp(Y<sub>j</sub>) </i> where
* \[
* dY_{j} = \mu_{j} dt + \lambda_{1,j} dW_{1} + \ldots + \lambda_{m,j} dW_{m}
* \]
* <br>
* The model uses an <code>AbstractLIBORCovarianceModel</code> for the specification of
* <i>(λ<sub>1,j</sub>,...,λ<sub>m,j</sub>)</i> as a covariance model.
* See {@link net.finmath.montecarlo.model.AbstractModelInterface} for details on the implemented interface
* <br><br>
* However, the class is more general:
* <ul>
* <li>
* The model may be log-normal or normal specification with a given local volatility.
* </li>
* <li>
* The class implements different measure(drift) / numeraire pairs: terminal measure and spot measure.
* </li>
* <li>
* The class allows to configure a discounting curve (e.g. for "OIS discounting") using a simple deterministic zero spread.
* In this case, the numeraire \( N(t) \) is adjusted by \( \exp( \int_0^t -\lambda(\tau) d\tau ) \).
* </li>
* </ul>
*
* <br>
* The class specifies a LIBOR market model, that is
* <i>L<sub>j</sub> = f(Y<sub>j</sub>) </i> where
* <ul>
* <li>
* <i>f</i> is <i>f(x) = exp(x)</i> (default, log-normal LIBOR Market Model) or
* </li>
* <li>
* <i>f</i> is <i>f(x) = x</i> (normal model, used if <code>property.set("stateSpace","NORMAL"))</code>
* </li>
* </ul>
* and
* <br>
* <i>dY<sub>j</sub> = μ<sub>j</sub> dt + λ<sub>1,j</sub> dW<sub>1</sub> + ... + λ<sub>m,j</sub> dW<sub>m</sub></i> <br>
* <br>
* see {@link net.finmath.montecarlo.model.AbstractModelInterface} for details on the implemented interface.
* <br>
* The model uses an <code>AbstractLIBORCovarianceModel</code> as a covariance model.
* If the covariance model is of type <code>AbstractLIBORCovarianceModelParametric</code>
* a calibration to swaptions can be performed.
* <br>
* Note that λ may still depend on <i>L</i>, hence generating a log-normal dynamic for <i>L</i> even
* if the stateSpace property has been set to NORMAL.
* <br>
*
* The map <code>properties</code> allows to configure the model. The following keys may be used:
* <ul>
* <li>
* <code>measure</code>: Possible values:
* <ul>
* <li>
* <code>SPOT</code>: Simulate under spot measure. In this case, the single curve numeraire
* is \( N(T_{i}) = \prod_{j=0}^{i-1} (1 + L(T_{j},T_{j+1};T_{j}) (T_{j+1}-T_{j})) \).
* </li>
* <li>
* <code>TERMINAL</code>: Simulate under terminal measure. In this case, the single curve numeraire
* is \( N(T_{i}) = P(T_{n};T_{i}) = \prod_{j=i}^{n-1} (1 + L(T_{j},T_{j+1};T_{i}) (T_{j+1}-T_{j}))^{-1} \).
* </li>
* </ul>
* </li>
* <li>
* <code>stateSpace</code>: Possible values:
* <ul>
* <li>
* <code>LOGNORMAL</code>: The state space transform is set to exp, i.e., <i>L = exp(Y)</i>. When the covariance model is deterministic, then this is the classical lognormal LIBOR market model. Note that the covariance model may still provide a local volatility function.
* </li>
* <li>
* <code>NORMAL</code>: The state space transform is set to identity, i.e., <i>L = Y</i>. When the covariance model is deterministic, then this is a normal LIBOR market model. Note that the covariance model may still provide a local volatility function.
* </li>
* </ul>
* </li>
* <li>
* <code>liborCap</code>: An optional <code>Double</code> 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 <code>liborCap</code> to <code>Double.POSITIVE_INFINITY</code>.
* </li>
* </ul>
* <br>
* 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. For the calibration
* of parametric models of the instantaneous covariance see
* {@link net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModelParametric#getCloneCalibrated(LIBORMarketModelInterface, AbstractLIBORMonteCarloProduct[], double[], double[], Map)}.
*
* @author Christian Fries
* @version 1.2
* @see net.finmath.montecarlo.process.AbstractProcessInterface The interface for numerical schemes.
* @see net.finmath.montecarlo.model.AbstractModelInterface The interface for models provinding parameters to numerical schemes.
* @see net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModel The abstract covariance model plug ins.
* @see net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModelParametric A parametic covariance model including a generic calibration algorithm.
*/
public class LIBORMarketModel extends AbstractModel implements LIBORMarketModelInterface {
public enum Driftapproximation { EULER, LINE_INTEGRAL, PREDICTOR_CORRECTOR }
public enum Measure { SPOT, TERMINAL }
public enum StateSpace { NORMAL, LOGNORMAL }
private final TimeDiscretizationInterface liborPeriodDiscretization;
private String forwardCurveName;
private AnalyticModelInterface curveModel;
private ForwardCurveInterface forwardRateCurve;
private DiscountCurveInterface discountCurve;
private AbstractLIBORCovarianceModel covarianceModel;
private AbstractSwaptionMarketData swaptionMarketData;
private Driftapproximation driftApproximationMethod = Driftapproximation.EULER;
private Measure measure = Measure.SPOT;
private StateSpace stateSpace = StateSpace.LOGNORMAL;
private double liborCap = 1E5;
// This is a cache of the integrated covariance.
private double[][][] integratedLIBORCovariance;
private final Object integratedLIBORCovarianceLazyInitLock = new Object();
// Cache for the numeraires, needs to be invalidated if process changes
private final ConcurrentHashMap<Integer, RandomVariableInterface> numeraires;
private AbstractProcessInterface numerairesProcess = null;
public static class CalibrationItem {
public final AbstractLIBORMonteCarloProduct calibrationProduct;
public final double calibrationTargetValue;
public final double calibrationWeight;
public CalibrationItem(AbstractLIBORMonteCarloProduct calibrationProduct, double calibrationTargetValue, double calibrationWeight) {
super();
this.calibrationProduct = calibrationProduct;
this.calibrationTargetValue = calibrationTargetValue;
this.calibrationWeight = calibrationWeight;
}
@Override
public String toString() {
return "CalibrationItem [calibrationProduct=" + calibrationProduct
+ ", calibrationTargetValue=" + calibrationTargetValue
+ ", calibrationWeight=" + calibrationWeight + "]";
}
}
/**
* Creates a LIBOR Market Model for given covariance.
* <br>
* 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.
* <br>
* The map <code>properties</code> allows to configure the model. The following keys may be used:
* <ul>
* <li>
* <code>measure</code>: Possible values:
* <ul>
* <li>
* <code>SPOT</code> (<code>String</code>): Simulate under spot measure.
* </li>
* <li>
* <code>TERMINAL</code> (<code>String</code>): Simulate under terminal measure.
* </li>
* </ul>
* </li>
* <li>
* <code>stateSpace</code>: Possible values:
* <ul>
* <li>
* <code>LOGNORMAL</code> (<code>String</code>): Simulate <i>L = exp(Y)</i>.
* </li>
* <li>
* <code>NORMAL</code> (<code>String</code>): Simulate <i>L = Y</i>.
* </li>
* </ul>
* </li>
* <li>
* <code>liborCap</code>: An optional <code>Double</code> 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 <code>liborCap</code> to <code>Double.POSITIVE_INFINITY</code>.
* </li>
* <li>
* <code>calibrationParameters</code>: Possible values:
* <ul>
* <li>
* <code>Map<String,Object></code> a parameter map with the following key/value pairs:
* <ul>
* <li>
* <code>accuracy</code>: <code>Double</code> specifying the required solver accuracy.
* </li>
* <li>
* <code>maxIterations</code>: <code>Integer</code> specifying the maximum iterations for the solver.
* </li>
* </ul>
* </li>
* </ul>
* </li>
* </ul>
*
* @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure).
* @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 calibrationItems 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 <code>measure</code> and <code>stateSpace</code>.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
public LIBORMarketModel(
TimeDiscretizationInterface liborPeriodDiscretization,
AnalyticModelInterface analyticModel,
ForwardCurveInterface forwardRateCurve,
DiscountCurveInterface discountCurve,
AbstractLIBORCovarianceModel covarianceModel,
CalibrationItem[] calibrationItems,
Map<String, ?> properties
) throws CalculationException {
// Set some properties
if(properties != null && properties.containsKey("measure")) measure = Measure.valueOf(((String)properties.get("measure")).toUpperCase());
if(properties != null && properties.containsKey("stateSpace")) stateSpace = StateSpace.valueOf(((String)properties.get("stateSpace")).toUpperCase());
if(properties != null && properties.containsKey("liborCap")) liborCap = (Double)properties.get("liborCap");
Map<String,Object> calibrationParameters = null;
if(properties != null && properties.containsKey("calibrationParameters")) calibrationParameters = (Map<String,Object>)properties.get("calibrationParameters");
this.liborPeriodDiscretization = liborPeriodDiscretization;
this.curveModel = analyticModel;
this.forwardRateCurve = forwardRateCurve;
this.discountCurve = discountCurve;
this.covarianceModel = covarianceModel;
double[] times = new double[liborPeriodDiscretization.getNumberOfTimeSteps()];
for(int i=0; i<times.length; i++) times[i] = liborPeriodDiscretization.getTime(i);
// Perform calibration, if data is given
if(calibrationItems != null && calibrationItems.length > 0) {
AbstractLIBORCovarianceModelParametric covarianceModelParametric = null;
try {
covarianceModelParametric = (AbstractLIBORCovarianceModelParametric)covarianceModel;
}
catch(Exception e) {
throw new ClassCastException("Calibration is currently restricted to parametric covariance models (AbstractLIBORCovarianceModelParametric).");
}
// @TODO Should be more elegant. Convert array for constructor
AbstractLIBORMonteCarloProduct[] calibrationProducts = new AbstractLIBORMonteCarloProduct[calibrationItems.length];
double[] calibrationTargetValues = new double[calibrationItems.length];
double[] calibrationWeights = new double[calibrationItems.length];
for(int i=0; i<calibrationTargetValues.length; i++) {
calibrationProducts[i] = calibrationItems[i].calibrationProduct;
calibrationTargetValues[i] = calibrationItems[i].calibrationTargetValue;
calibrationWeights[i] = calibrationItems[i].calibrationWeight;
}
this.covarianceModel = covarianceModelParametric.getCloneCalibrated(this, calibrationProducts, calibrationTargetValues, calibrationWeights, calibrationParameters);
}
numeraires = new ConcurrentHashMap<Integer, RandomVariableInterface>();
}
/**
* Creates a LIBOR Market Model for given covariance.
*
* @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure).
* @param forwardRateCurve The initial values for the forward rates.
* @param covarianceModel The covariance model to use.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
public LIBORMarketModel(
TimeDiscretizationInterface liborPeriodDiscretization,
ForwardCurveInterface forwardRateCurve,
AbstractLIBORCovarianceModel covarianceModel
) throws CalculationException {
this(liborPeriodDiscretization, forwardRateCurve, new DiscountCurveFromForwardCurve(forwardRateCurve), covarianceModel, new CalibrationItem[0], null);
}
/**
* Creates a LIBOR Market Model for given covariance.
*
* @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure).
* @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.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
public LIBORMarketModel(
TimeDiscretizationInterface liborPeriodDiscretization,
ForwardCurveInterface forwardRateCurve,
DiscountCurveInterface discountCurve,
AbstractLIBORCovarianceModel covarianceModel
) throws CalculationException {
this(liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, new CalibrationItem[0], null);
}
/**
* Creates a LIBOR Market Model using a given covariance model and calibrating this model
* to given swaption volatility data.
*
* @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure).
* @param forwardRateCurve The initial values for the forward rates.
* @param covarianceModel The covariance model to use.
* @param swaptionMarketData The set of swaption values to calibrate to.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
public LIBORMarketModel(
TimeDiscretizationInterface liborPeriodDiscretization,
ForwardCurveInterface forwardRateCurve,
AbstractLIBORCovarianceModel covarianceModel,
AbstractSwaptionMarketData swaptionMarketData
) throws CalculationException {
this(liborPeriodDiscretization, forwardRateCurve, new DiscountCurveFromForwardCurve(forwardRateCurve), covarianceModel, swaptionMarketData, null);
}
/**
* Creates a LIBOR Market Model for given covariance.
*
* @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure).
* @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 swaptionMarketData The set of swaption values to calibrate to.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
public LIBORMarketModel(
TimeDiscretizationInterface liborPeriodDiscretization,
ForwardCurveInterface forwardRateCurve,
DiscountCurveInterface discountCurve,
AbstractLIBORCovarianceModel covarianceModel,
AbstractSwaptionMarketData swaptionMarketData
) throws CalculationException {
this(
liborPeriodDiscretization,
forwardRateCurve,
discountCurve,
covarianceModel,
swaptionMarketData,
null
);
}
/**
* Creates a LIBOR Market Model for given covariance.
*
* @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure).
* @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 swaptionMarketData The set of swaption values to calibrate to.
* @param properties Key value map specifying properties like <code>measure</code> and <code>stateSpace</code>.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
public LIBORMarketModel(
TimeDiscretizationInterface liborPeriodDiscretization,
ForwardCurveInterface forwardRateCurve,
DiscountCurveInterface discountCurve,
AbstractLIBORCovarianceModel covarianceModel,
AbstractSwaptionMarketData swaptionMarketData,
Map<String, ?> properties
) throws CalculationException {
this(
liborPeriodDiscretization,
forwardRateCurve,
discountCurve,
covarianceModel,
getCalibrationItems(
liborPeriodDiscretization,
forwardRateCurve,
swaptionMarketData,
// Condition under which we use analytic approximation
(properties == null || properties.get("stateSpace") == null || ((String)properties.get("stateSpace")).toUpperCase().equals(StateSpace.LOGNORMAL.name()))
&& AbstractLIBORCovarianceModelParametric.class.isAssignableFrom(covarianceModel.getClass())
),
properties
);
}
/**
* Creates a LIBOR Market Model for given covariance.
* <br>
* 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.
* <br>
* The map <code>properties</code> allows to configure the model. The following keys may be used:
* <ul>
* <li>
* <code>measure</code>: Possible values:
* <ul>
* <li>
* <code>SPOT</code> (<code>String</code>): Simulate under spot measure.
* </li>
* <li>
* <code>TERMINAL</code> (<code>String</code>): Simulate under terminal measure.
* </li>
* </ul>
* </li>
* <li>
* <code>stateSpace</code>: Possible values:
* <ul>
* <li>
* <code>LOGNORMAL</code> (<code>String</code>): Simulate <i>L = exp(Y)</i>.
* </li>
* <li>
* <code>NORMAL</code> (<code>String</code>): Simulate <i>L = Y</i>.
* </li>
* </ul>
* </li>
* <li>
* <code>calibrationParameters</code>: Possible values:
* <ul>
* <li>
* <code>Map<String,Object></code> a parameter map with the following key/value pairs:
* <ul>
* <li>
* <code>accuracy</code>: <code>Double</code> specifying the required solver accuracy.
* </li>
* <li>
* <code>maxIterations</code>: <code>Integer</code> specifying the maximum iterations for the solver.
* </li>
* </ul>
* </li>
* </ul>
* </li>
* </ul>
*
* @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure).
* @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 calibrationItems 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 <code>measure</code> and <code>stateSpace</code>.
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
public LIBORMarketModel(
TimeDiscretizationInterface liborPeriodDiscretization,
ForwardCurveInterface forwardRateCurve,
DiscountCurveInterface discountCurve,
AbstractLIBORCovarianceModel covarianceModel,
CalibrationItem[] calibrationItems,
Map<String, ?> properties
) throws CalculationException {
this(liborPeriodDiscretization, null, forwardRateCurve, discountCurve, covarianceModel, calibrationItems, properties);
}
private static CalibrationItem[] getCalibrationItems(TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardCurve, AbstractSwaptionMarketData swaptionMarketData, boolean isUseAnalyticApproximation) {
if(swaptionMarketData == null) return null;
TimeDiscretizationInterface optionMaturities = swaptionMarketData.getOptionMaturities();
TimeDiscretizationInterface tenor = swaptionMarketData.getTenor();
double swapPeriodLength = swaptionMarketData.getSwapPeriodLength();
ArrayList<CalibrationItem> calibrationItems = new ArrayList<CalibrationItem>();
for(int exerciseIndex=0; exerciseIndex<=optionMaturities.getNumberOfTimeSteps(); exerciseIndex++) {
for(int tenorIndex=0; tenorIndex<=tenor.getNumberOfTimeSteps()-exerciseIndex; tenorIndex++) {
// Create a swaption
double exerciseDate = optionMaturities.getTime(exerciseIndex);
double swapLength = tenor.getTime(tenorIndex);
if(liborPeriodDiscretization.getTimeIndex(exerciseDate) < 0) continue;
if(liborPeriodDiscretization.getTimeIndex(exerciseDate+swapLength) <= liborPeriodDiscretization.getTimeIndex(exerciseDate)) continue;
int numberOfPeriods = (int)(swapLength / swapPeriodLength);
double[] fixingDates = new double[numberOfPeriods];
double[] paymentDates = new double[numberOfPeriods];
double[] swapTenorTimes = new double[numberOfPeriods+1];
for(int periodStartIndex=0; periodStartIndex<numberOfPeriods; periodStartIndex++) {
fixingDates[periodStartIndex] = exerciseDate + periodStartIndex * swapPeriodLength;
paymentDates[periodStartIndex] = exerciseDate + (periodStartIndex+1) * swapPeriodLength;
swapTenorTimes[periodStartIndex] = exerciseDate + periodStartIndex * swapPeriodLength;
}
swapTenorTimes[numberOfPeriods] = exerciseDate + numberOfPeriods * swapPeriodLength;
// Swaptions swap rate
ScheduleInterface swapTenor = new RegularSchedule(new TimeDiscretization(swapTenorTimes));
double swaprate = Swap.getForwardSwapRate(swapTenor, swapTenor, forwardCurve, null);
// Set swap rates for each period
double[] swaprates = new double[numberOfPeriods];
for(int periodStartIndex=0; periodStartIndex<numberOfPeriods; periodStartIndex++) {
swaprates[periodStartIndex] = swaprate;
}
if(isUseAnalyticApproximation) {
AbstractLIBORMonteCarloProduct swaption = new SwaptionAnalyticApproximation(swaprate, swapTenorTimes, SwaptionAnalyticApproximation.ValueUnit.VOLATILITY);
double impliedVolatility = swaptionMarketData.getVolatility(exerciseDate, swapLength, swaptionMarketData.getSwapPeriodLength(), swaprate);
calibrationItems.add(new CalibrationItem(swaption, impliedVolatility, 1.0));
}
else {
AbstractLIBORMonteCarloProduct swaption = new SwaptionSimple(swaprate, swapTenorTimes, SwaptionSimple.ValueUnit.VALUE);
double forwardSwaprate = Swap.getForwardSwapRate(swapTenor, swapTenor, forwardCurve);
double swapAnnuity = SwapAnnuity.getSwapAnnuity(swapTenor, forwardCurve);
double impliedVolatility = swaptionMarketData.getVolatility(exerciseDate, swapLength, swaptionMarketData.getSwapPeriodLength(), swaprate);
double targetValue = AnalyticFormulas.blackModelSwaptionValue(forwardSwaprate, impliedVolatility, exerciseDate, swaprate, swapAnnuity);
calibrationItems.add(new CalibrationItem(swaption, targetValue, 1.0));
}
}
}
return calibrationItems.toArray(new CalibrationItem[calibrationItems.size()]);
}
/**
* 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 <i>t</i> for which the numeraire should be returned <i>N(t)</i>.
* @return The numeraire at the specified time as <code>RandomVariableInterface</code>
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
@Override
public RandomVariableInterface getNumeraire(double time) throws CalculationException {
int timeIndex = getLiborPeriodIndex(time);
if(timeIndex < 0) {
// Interpolation of Numeraire: log linear interpolation.
int upperIndex = -timeIndex-1;
int lowerIndex = upperIndex-1;
if(lowerIndex < 0) throw new IllegalArgumentException("Numeraire requested for time " + time + ". Unsupported");
double alpha = (time-getLiborPeriod(lowerIndex)) / (getLiborPeriod(upperIndex) - getLiborPeriod(lowerIndex));
RandomVariableInterface numeraire = getNumeraire(getLiborPeriod(upperIndex)).log().mult(alpha).add(getNumeraire(getLiborPeriod(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
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(getProcess() != numerairesProcess) {
numeraires.clear();
numerairesProcess = getProcess();
}
/*
* Check if numeraire is part of the cache
*/
RandomVariableInterface numeraire = numeraires.get(timeIndex);
if(numeraire == null) {
/*
* Calculate the numeraire for timeIndex
*/
// Initialize to 1.0
numeraire = getProcess().getStochasticDriver().getRandomVariableForConstant(1.0);
// Get the start and end of the product
int firstLiborIndex, lastLiborIndex;
if(measure == Measure.TERMINAL) {
firstLiborIndex = getLiborPeriodIndex(time);
if(firstLiborIndex < 0) {
throw new CalculationException("Simulation time discretization not part of forward rate tenor discretization.");
}
lastLiborIndex = liborPeriodDiscretization.getNumberOfTimeSteps()-1;
}
else if(measure == Measure.SPOT) {
// Spot measure
firstLiborIndex = 0;
lastLiborIndex = getLiborPeriodIndex(time)-1;
}
else {
throw new CalculationException("Numeraire not implemented for specified measure.");
}
// The product
for(int liborIndex = firstLiborIndex; liborIndex<=lastLiborIndex; liborIndex++) {
RandomVariableInterface libor = getLIBOR(getTimeIndex(Math.min(time,liborPeriodDiscretization.getTime(liborIndex))), liborIndex);
double periodLength = liborPeriodDiscretization.getTimeStep(liborIndex);
if(measure == Measure.SPOT) {
numeraire = numeraire.accrue(libor, periodLength);
}
else {
numeraire = numeraire.discount(libor, periodLength);
}
}
numeraires.put(timeIndex, numeraire);
}
/*
* Adjust for discounting, i.e. funding or collateralization
*/
if(discountCurve != null) {
// This includes a control for zero bonds
double deterministicNumeraireAdjustment = numeraire.invert().getAverage() / discountCurve.getDiscountFactor(curveModel, time);
numeraire = numeraire.mult(deterministicNumeraireAdjustment);
}
return numeraire;
}
@Override
public RandomVariableInterface[] getInitialState() {
double[] liborInitialStates = new double[liborPeriodDiscretization.getNumberOfTimeSteps()];
for(int timeIndex=0; timeIndex<liborPeriodDiscretization.getNumberOfTimeSteps(); timeIndex++) {
// double rate = forwardRateCurve.getForward(curveModel, liborPeriodDiscretization.getTime(timeIndex), liborPeriodDiscretization.getTimeStep(timeIndex));
double rate = forwardRateCurve.getForward(curveModel, liborPeriodDiscretization.getTime(timeIndex));
liborInitialStates[timeIndex] = (stateSpace == StateSpace.LOGNORMAL) ? Math.log(Math.max(rate,0)) : rate;
}
RandomVariableInterface[] initialStateRandomVariable = new RandomVariableInterface[getNumberOfComponents()];
for(int componentIndex=0; componentIndex<getNumberOfComponents(); componentIndex++) {
initialStateRandomVariable[componentIndex] = getProcess().getStochasticDriver().getRandomVariableForConstant(liborInitialStates[componentIndex]);
}
return initialStateRandomVariable;
}
/**
* Return the complete vector of the drift for the time index timeIndex, given that current state is realizationAtTimeIndex.
* The drift will be zero for rates being already fixed.
*
* The method currently provides the drift for either <code>Measure.SPOT</code> or <code>Measure.TERMINAL</code> - depending how the
* model object was constructed. For <code>Measure.TERMINAL</code> 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 <code>Measure.SPOT</code> 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.LIBORMarketModel#getNumeraire(double) The calculation of the drift is consistent with the calculation of the numeraire in <code>getNumeraire</code>.
* @see net.finmath.montecarlo.interestrate.LIBORMarketModel#getFactorLoading(int, int, RandomVariableInterface[]) The factor loading \( \lambda_{j,k} \).
*
* @param timeIndex Time index <i>i</i> for which the drift should be returned <i>μ(t<sub>i</sub>)</i>.
* @param realizationAtTimeIndex Time current forward rate vector at time index <i>i</i> which should be used in the calculation.
* @return The drift vector μ(t<sub>i</sub>) as <code>RandomVariable[]</code>
*/
@Override
public RandomVariableInterface[] getDrift(int timeIndex, RandomVariableInterface[] realizationAtTimeIndex, RandomVariableInterface[] realizationPredictor) {
double time = getTime(timeIndex);
int firstLiborIndex = this.getLiborPeriodIndex(time)+1;
if(firstLiborIndex<0) firstLiborIndex = -firstLiborIndex-1 + 1;
RandomVariableInterface zero = getProcess().getStochasticDriver().getRandomVariableForConstant(0.0);
// Allocate drift vector and initialize to zero (will be used to sum up drift components)
RandomVariableInterface[] drift = new RandomVariableInterface[getNumberOfComponents()];
for(int componentIndex=firstLiborIndex; componentIndex<getNumberOfComponents(); componentIndex++) {
drift[componentIndex] = zero;
}
RandomVariableInterface[] covarianceFactorSums = new RandomVariableInterface[getNumberOfFactors()];
for(int factorIndex=0; factorIndex<getNumberOfFactors(); factorIndex++) {
covarianceFactorSums[factorIndex] = zero;
}
if(measure == Measure.SPOT) {
// Calculate drift for the component componentIndex (starting at firstLiborIndex, others are zero)
for(int componentIndex=firstLiborIndex; componentIndex<getNumberOfComponents(); componentIndex++) {
double periodLength = liborPeriodDiscretization.getTimeStep(componentIndex);
RandomVariableInterface libor = realizationAtTimeIndex[componentIndex];
RandomVariableInterface oneStepMeasureTransform = (getProcess().getStochasticDriver().getRandomVariableForConstant(periodLength)).discount(libor, periodLength);
if(stateSpace == StateSpace.LOGNORMAL) oneStepMeasureTransform = oneStepMeasureTransform.mult(libor);
RandomVariableInterface[] factorLoading = getFactorLoading(timeIndex, componentIndex, realizationAtTimeIndex);
for(int factorIndex=0; factorIndex<getNumberOfFactors(); factorIndex++) {
covarianceFactorSums[factorIndex] = covarianceFactorSums[factorIndex].add(oneStepMeasureTransform.mult(factorLoading[factorIndex]));
drift[componentIndex] = drift[componentIndex].addProduct(covarianceFactorSums[factorIndex], factorLoading[factorIndex]);
}
}
}
else if(measure == Measure.TERMINAL) {
// Calculate drift for the component componentIndex (starting at firstLiborIndex, others are zero)
for(int componentIndex=getNumberOfComponents()-1; componentIndex>=firstLiborIndex; componentIndex--) {
double periodLength = liborPeriodDiscretization.getTimeStep(componentIndex);
RandomVariableInterface libor = realizationAtTimeIndex[componentIndex];
RandomVariableInterface oneStepMeasureTransform = (getProcess().getStochasticDriver().getRandomVariableForConstant(periodLength)).discount(libor, periodLength);
if(stateSpace == StateSpace.LOGNORMAL) oneStepMeasureTransform = oneStepMeasureTransform.mult(libor);
RandomVariableInterface[] factorLoading = getFactorLoading(timeIndex, componentIndex, realizationAtTimeIndex);
for(int factorIndex=0; factorIndex<getNumberOfFactors(); factorIndex++) {
drift[componentIndex] = drift[componentIndex].addProduct(covarianceFactorSums[factorIndex], factorLoading[factorIndex]);
covarianceFactorSums[factorIndex] = covarianceFactorSums[factorIndex].sub(oneStepMeasureTransform.mult(factorLoading[factorIndex]));
}
}
}
if(stateSpace == StateSpace.LOGNORMAL) {
// Drift adjustment for log-coordinate in each component
for(int componentIndex=firstLiborIndex; componentIndex<getNumberOfComponents(); componentIndex++) {
RandomVariableInterface variance = covarianceModel.getCovariance(getTime(timeIndex), componentIndex, componentIndex, realizationAtTimeIndex);
drift[componentIndex] = drift[componentIndex].addProduct(variance, -0.5);
}
}
return drift;
}
@Override
public RandomVariableInterface[] getFactorLoading(int timeIndex, int componentIndex, RandomVariableInterface[] realizationAtTimeIndex)
{
return covarianceModel.getFactorLoading(getTime(timeIndex), getLiborPeriod(componentIndex), realizationAtTimeIndex);
}
@Override
public RandomVariableInterface applyStateSpaceTransform(int componentIndex, RandomVariableInterface randomVariable) {
RandomVariableInterface value = randomVariable;
if(stateSpace == StateSpace.LOGNORMAL) value = value.exp();
if(!Double.isInfinite(liborCap)) value = value.cap(liborCap);
return value;
}
/**
* @return Returns the driftApproximationMethod.
*/
public Driftapproximation getDriftApproximationMethod() {
return driftApproximationMethod;
}
@Override
public RandomVariableInterface getLIBOR(double time, double periodStart, double periodEnd) throws CalculationException
{
int periodStartIndex = getLiborPeriodIndex(periodStart);
int periodEndIndex = getLiborPeriodIndex(periodEnd);
// The forward rates are provided on fractional tenor discretization points using linear interpolation. See ISBN 0470047224.
// Interpolation on tenor, consistent with interpolation on numeraire (log-linear): interpolate end date
if(periodEndIndex < 0) {
int previousEndIndex = (-periodEndIndex-1)-1;
double previousEndTime = getLiborPeriod(previousEndIndex);
double nextEndTime = getLiborPeriod(previousEndIndex+1);
RandomVariableInterface liborLongPeriod = getLIBOR(time, periodStart, nextEndTime);
RandomVariableInterface liborShortPeriod = getLIBOR(time, previousEndTime, nextEndTime);
// Interpolate libor from periodStart to periodEnd on periodEnd
RandomVariableInterface libor = liborLongPeriod.mult(nextEndTime-periodStart).add(1.0)
.div(
liborShortPeriod.mult(nextEndTime-previousEndTime).add(1.0).log().mult((nextEndTime-periodEnd)/(nextEndTime-previousEndTime)).exp()
).sub(1.0).div(periodEnd-periodStart);
// Analytic adjustment for the interpolation
// @TODO reference to AnalyticModel must not be null
// @TODO This adjustment only applies if the corresponding adjustment in getNumeraire is enabled
double analyticLibor = getForwardRateCurve().getForward(getAnalyticModel(), previousEndTime, periodEnd-previousEndTime);
double analyticLiborShortPeriod = getForwardRateCurve().getForward(getAnalyticModel(), previousEndTime, nextEndTime-previousEndTime);
double analyticInterpolatedOnePlusLiborDt = (1 + analyticLiborShortPeriod * (nextEndTime-previousEndTime)) / Math.exp(Math.log(1 + analyticLiborShortPeriod * (nextEndTime-previousEndTime)) * (nextEndTime-periodEnd)/(nextEndTime-previousEndTime));
double analyticOnePlusLiborDt = (1 + analyticLibor * (periodEnd-previousEndTime));
double adjustment = analyticOnePlusLiborDt / analyticInterpolatedOnePlusLiborDt;
libor = libor.mult(periodEnd-periodStart).add(1.0).mult(adjustment).sub(1.0).div(periodEnd-periodStart);
return libor;
}
// Interpolation on tenor, consistent with interpolation on numeraire (log-linear): interpolate start date
if(periodStartIndex < 0) {
int previousStartIndex = (-periodStartIndex-1)-1;
double previousStartTime = getLiborPeriod(previousStartIndex);
double nextStartTime = getLiborPeriod(previousStartIndex+1);
RandomVariableInterface liborLongPeriod = getLIBOR(time, previousStartTime, periodEnd);
RandomVariableInterface liborShortPeriod = getLIBOR(time, previousStartTime, nextStartTime);
RandomVariableInterface libor = liborLongPeriod.mult(periodEnd-previousStartTime).add(1.0)
.div(
liborShortPeriod.mult(nextStartTime-previousStartTime).add(1.0).log().mult((periodStart-previousStartTime)/(nextStartTime-previousStartTime)).exp()
).sub(1.0).div(periodEnd-periodStart);
// Analytic adjustment for the interpolation
// @TODO reference to AnalyticModel must not be null
// @TODO This adjustment only applies if the corresponding adjustment in getNumeraire is enabled
double analyticLibor = getForwardRateCurve().getForward(getAnalyticModel(), previousStartTime, nextStartTime-periodStart);
double analyticLiborShortPeriod = getForwardRateCurve().getForward(getAnalyticModel(), previousStartTime, nextStartTime-previousStartTime);
double analyticInterpolatedOnePlusLiborDt = (1 + analyticLiborShortPeriod * (nextStartTime-previousStartTime)) / Math.exp(Math.log(1 + analyticLiborShortPeriod * (nextStartTime-previousStartTime)) * (nextStartTime-periodStart)/(nextStartTime-previousStartTime));
double analyticOnePlusLiborDt = (1 + analyticLibor * (periodStart-previousStartTime));
double adjustment = analyticOnePlusLiborDt / analyticInterpolatedOnePlusLiborDt;
libor = libor.mult(periodEnd-periodStart).add(1.0).div(adjustment).sub(1.0).div(periodEnd-periodStart);
return libor;
}
if(periodStartIndex < 0 || periodEndIndex < 0) throw new AssertionError("LIBOR requested outside libor discretization points and interpolation was not performed.");
// If time is beyond fixing, use the fixing time.
time = Math.min(time, periodStart);
int timeIndex = getTimeIndex(time);
// If time is not part of the discretization, use the latest available point.
if(timeIndex < 0) {
timeIndex = -timeIndex-2;
// double timeStep = getTimeDiscretization().getTimeStep(timeIndex);
// return getLIBOR(getTime(timeIndex), periodStart, periodEnd).mult((getTime(timeIndex+1)-time)/timeStep).add(getLIBOR(getTime(timeIndex+1), periodStart, periodEnd).mult((time-getTime(timeIndex))/timeStep));
}
// If this is a model primitive then return it
if(periodStartIndex+1==periodEndIndex) return getLIBOR(timeIndex, periodStartIndex);
// The requested LIBOR is not a model primitive. We need to calculate it (slow!)
RandomVariableInterface accrualAccount = getProcess().getStochasticDriver().getRandomVariableForConstant(1.0);
// Calculate the value of the forward bond
for(int periodIndex = periodStartIndex; periodIndex<periodEndIndex; periodIndex++)
{
double subPeriodLength = getLiborPeriod(periodIndex+1) - getLiborPeriod(periodIndex);
RandomVariableInterface liborOverSubPeriod = getLIBOR(timeIndex, periodIndex);
accrualAccount = accrualAccount.accrue(liborOverSubPeriod, subPeriodLength);
}
RandomVariableInterface libor = accrualAccount.sub(1.0).div(periodEnd - periodStart);
return libor;
}
@Override
public RandomVariableInterface getLIBOR(int timeIndex, int liborIndex) throws CalculationException
{
// This method is just a synonym - call getProcessValue of super class
return getProcessValue(timeIndex, liborIndex);
}
@Override
public int getNumberOfComponents() {
return liborPeriodDiscretization.getNumberOfTimeSteps();
}
@Override
public int getNumberOfLibors()
{
// This is just a synonym to number of components
return getNumberOfComponents();
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getLiborPeriod(int)
*/
@Override
public double getLiborPeriod(int timeIndex) {
if(timeIndex >= liborPeriodDiscretization.getNumberOfTimes() || timeIndex < 0) {
throw new ArrayIndexOutOfBoundsException("Index for LIBOR period discretization out of bounds: " + timeIndex + ".");
}
return liborPeriodDiscretization.getTime(timeIndex);
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getLiborPeriodIndex(double)
*/
@Override
public int getLiborPeriodIndex(double time) {
return liborPeriodDiscretization.getTimeIndex(time);
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getLiborPeriodDiscretization()
*/
@Override
public TimeDiscretizationInterface getLiborPeriodDiscretization() {
return liborPeriodDiscretization;
}
/**
* @return Returns the measure.
*/
public Measure getMeasure() {
return measure;
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getIntegratedLIBORCovariance()
*/
@Override
public double[][][] getIntegratedLIBORCovariance() {
synchronized (integratedLIBORCovarianceLazyInitLock) {
if(integratedLIBORCovariance == null) {
TimeDiscretizationInterface liborPeriodDiscretization = getLiborPeriodDiscretization();
TimeDiscretizationInterface simulationTimeDiscretization = getCovarianceModel().getTimeDiscretization();
integratedLIBORCovariance = new double[simulationTimeDiscretization.getNumberOfTimeSteps()][liborPeriodDiscretization.getNumberOfTimeSteps()][liborPeriodDiscretization.getNumberOfTimeSteps()];
for(int timeIndex = 0; timeIndex < simulationTimeDiscretization.getNumberOfTimeSteps(); timeIndex++) {
double dt = simulationTimeDiscretization.getTime(timeIndex+1) - simulationTimeDiscretization.getTime(timeIndex);
RandomVariableInterface[][] factorLoadings = new RandomVariableInterface[liborPeriodDiscretization.getNumberOfTimeSteps()][];
// Prefetch factor loadings
for(int componentIndex = 0; componentIndex < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex++) {
factorLoadings[componentIndex] = getCovarianceModel().getFactorLoading(timeIndex, componentIndex, null);
}
for(int componentIndex1 = 0; componentIndex1 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex1++) {
RandomVariableInterface[] factorLoadingOfComponent1 = factorLoadings[componentIndex1];
// Sum the libor cross terms (use symmetry)
for(int componentIndex2 = componentIndex1; componentIndex2 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex2++) {
double integratedLIBORCovarianceValue = 0.0;
if(getLiborPeriod(componentIndex1) > getTime(timeIndex)) {
RandomVariableInterface[] factorLoadingOfComponent2 = factorLoadings[componentIndex2];
for(int factorIndex = 0; factorIndex < getNumberOfFactors(); factorIndex++) {
integratedLIBORCovarianceValue += factorLoadingOfComponent1[factorIndex].get(0) * factorLoadingOfComponent2[factorIndex].get(0) * dt;
}
}
integratedLIBORCovariance[timeIndex][componentIndex1][componentIndex2] = integratedLIBORCovarianceValue;
}
}
}
// Integrate over time (i.e. sum up).
for(int timeIndex = 1; timeIndex < simulationTimeDiscretization.getNumberOfTimeSteps(); timeIndex++) {
double[][] prevIntegratedLIBORCovariance = integratedLIBORCovariance[timeIndex-1];
double[][] thisIntegratedLIBORCovariance = integratedLIBORCovariance[timeIndex];
for(int componentIndex1 = 0; componentIndex1 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex1++) {
for(int componentIndex2 = componentIndex1; componentIndex2 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex2++) {
thisIntegratedLIBORCovariance[componentIndex1][componentIndex2] = prevIntegratedLIBORCovariance[componentIndex1][componentIndex2] + thisIntegratedLIBORCovariance[componentIndex1][componentIndex2];
thisIntegratedLIBORCovariance[componentIndex2][componentIndex1] = thisIntegratedLIBORCovariance[componentIndex1][componentIndex2];
}
}
}
}
}
return integratedLIBORCovariance;
}
@Override
public Object clone() {
try {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("measure", measure.name());
properties.put("stateSpace", stateSpace.name());
return new LIBORMarketModel(getLiborPeriodDiscretization(), getAnalyticModel(), getForwardRateCurve(), getDiscountCurve(), covarianceModel, new CalibrationItem[0], properties);
} catch (CalculationException e) {
return null;
}
}
@Override
public AnalyticModelInterface getAnalyticModel() {
return curveModel;
}
@Override
public DiscountCurveInterface getDiscountCurve() {
return discountCurve;
}
@Override
public ForwardCurveInterface getForwardRateCurve() {
return forwardRateCurve;
}
/**
* Return the swaption market data used for calibration (if any, may be null).
*
* @return The swaption market data used for calibration (if any, may be null).
*/
public AbstractSwaptionMarketData getSwaptionMarketData() {
return swaptionMarketData;
}
/* (non-Javadoc)
* @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getCovarianceModel()
*/
@Override
public AbstractLIBORCovarianceModel getCovarianceModel() {
return covarianceModel;
}
/**
* @param covarianceModel A covariance model
* @return A new <code>LIBORMarketModel</code> using the specified covariance model.
*/
@Override
public LIBORMarketModel getCloneWithModifiedCovarianceModel(AbstractLIBORCovarianceModel covarianceModel) {
LIBORMarketModel model = (LIBORMarketModel)this.clone();
model.covarianceModel = covarianceModel;
return model;
}
@Override
public LIBORMarketModel getCloneWithModifiedData(Map<String, Object> dataModified) throws CalculationException {
TimeDiscretizationInterface liborPeriodDiscretization = this.liborPeriodDiscretization;
AnalyticModelInterface analyticModel = this.curveModel;
ForwardCurveInterface forwardRateCurve = this.forwardRateCurve;
DiscountCurveInterface discountCurve = this.discountCurve;
AbstractLIBORCovarianceModel covarianceModel = this.covarianceModel;
AbstractSwaptionMarketData swaptionMarketData = null; // No recalibration, unless new swaption data is specified
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("measure", measure.name());
properties.put("stateSpace", stateSpace.name());
if(dataModified.containsKey("liborPeriodDiscretization")) {
liborPeriodDiscretization = (TimeDiscretizationInterface)dataModified.get("liborPeriodDiscretization");
}
if(dataModified.containsKey("forwardRateCurve")) {
forwardRateCurve = (ForwardCurveInterface)dataModified.get("forwardRateCurve");
}
if(dataModified.containsKey("discountCurve")) {
discountCurve = (DiscountCurveInterface)dataModified.get("discountCurve");
}
if(dataModified.containsKey("forwardRateShift")) {
throw new RuntimeException("Forward rate shift clone currently disabled.");
}
if(dataModified.containsKey("covarianceModel")) {
covarianceModel = (AbstractLIBORCovarianceModel)dataModified.get("covarianceModel");
}
if(dataModified.containsKey("swaptionMarketData")) {
swaptionMarketData = (AbstractSwaptionMarketData)dataModified.get("swaptionMarketData");
}
LIBORMarketModel newModel = new LIBORMarketModel(liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, swaptionMarketData, properties);
newModel.curveModel = analyticModel;
return newModel;
}
@Override
public String toString() {
return "LIBORMarketModel [liborPeriodDiscretization="
+ liborPeriodDiscretization + ", forwardCurveName="
+ forwardCurveName + ", curveModel=" + curveModel
+ ", forwardRateCurve=" + forwardRateCurve + ", discountCurve="
+ discountCurve + ", covarianceModel=" + covarianceModel
+ ", driftApproximationMethod=" + driftApproximationMethod
+ ", measure=" + measure + ", stateSpace=" + stateSpace + "]";
}
}